# install.packages('pak')
::pak('mjfrigaard/shinypak') pak
26 rhino
This chapter briefly describes a version of sap
built using rhino
. The resulting app (rap
) is in the 21_rhino
branch.
The branch in this chapter is slightly different than the previous golem
and leprechaun
branches, because instead of loading, documenting, and installing rap
, we’re going to re-initialize the IDE by selecting Session > Terminate R…
When the IDE re-opens, we see the rap
files and notice the Build pane has been removed:
The Build pane is deactivated because rhino
applications aren’t R packages.1
Launch the application in rap
by opening the app.R
file and clicking Run App (or by passing rhino::app()
into the Console).
Launch app with the shinypak
package:
launch('21_rhino')
26.1 rap
(a rhino
app)
The files in rap
are below:
├── .Rprofile
├── .github/
│ └── workflows
├── .gitignore
├── .lintr
├── .renvignore
├── .rscignore
├── README.md
├── app
│ ├── js
│ ├── logic
│ ├── main.R
│ ├── static
│ ├── styles
│ └── view
├── app.R
├── config.yml
├── dependencies.R.
├── sap.Rproj
├── renv
│ ├── .gitignore.
│ ├── activate.R
│ ├── library
│ ├── settings.json
│ └── staging
├── renv.lock
├── rhino.yml
└── tests
├── cypress
├── cypress.json
└── testthat
24 directories, 31 files
- 1
-
Activates the
renv
package - 2
-
CI/CD via GitHub actions
- 3
-
Lintr (from
lintr
package)
- 4
-
renv
ignore (works like.gitignore
)
- 5
-
rhino
app dependencies - 6
-
renv
library of packages in app project
As we can see, most of the standard R package folders and files are missing from rap
, because rhino
applications use the box
package for importing dependencies and organizing code.2
26.2 rhino
features
The rhino
website explains the philosophy behind the application structure above, so I won’t repeat that information here. However, I highly recommend reading the available documentation on rhino
and box
before deciding to adopt this framework.3
26.3 box
modules
A box
module (not to be confused with a Shiny module) is a collection of .R
scripts in a specified folder. The modules in a new rhino
app are stored in the app/logic/
and app/view/
folders:4
app
├── js/
├── logic/
├── main.R
├── static/
├── styles/
└── view/
6 directories, 1 file
- 1
- JavaScript code
- 2
-
Non-shiny code
- 3
-
Primary app file
- 4
-
Static
.js
or.css
- 5
- App CSS files
- 6
- Shiny modules and app code
26.3.1 Utility functions
In rap
, I’ve placed the non-Shiny utility functions (i.e., the business logic) in app/logic
:
app/logic
├── __init__.R
├── data.R
└── plot.R
1 directory, 4 files
- 1
-
Load
movies
data
- 2
-
scatter_plot()
utility function
26.3.2 Shiny modules
Our Shiny box
modules are placed in app/view
, and separated into inputs
and display
:
app/view
├── __init__.R
├── display.R
└── inputs.R
1 directory, 3 files
- 1
-
similar to the code from
R/mod_var_input.R
- 2
-
similar to the code from
R/mod_scatter_display.R
app/view/inputs
collects and returns the reactive values from the UI. The app/view/display
module includes the app/logic/data
and app/logic/plot
modules.
# app/view/display.R
# import data and plot modules
::use(
box/ logic / data,
app / logic / plot
app )
26.4 app/main.R
The app/main.R
file contains the primary UI and Server functions for the application. This file adds the shiny
functions and the inputs
and display
modules from app/view
:
# app/main.R
# shiny functions
::use(
box
shiny[
NS, fluidPage, sidebarLayout, sidebarPanel,
mainPanel, fluidRow, column, tags, icon,
textOutput, moduleServer, renderText
]
)
# import modules
::use(
box# load inputs module ----
/ view / inputs,
app # load display module ----
/ view / display
app )
Note that we don’t need to import app/logic
modules in app/main.R
, because they’re imported in their respective app/view
modules.
26.5 Tests
rhino
apps have support for testing with testthat
, shiny::testServer()
, shinytest2
, and Cypress.
tests/
├── cypress
│ └── integration
│ └── app.spec.js
├── cypress.json
└── testthat
└── test-main.R
4 directories, 3 files
- 1
-
Cypress test infrastructure
- 2
-
testthat
test infrastructure
Below is the boilerplate test code in the tests/testthat/test-main.R
file:
::use(
box
shiny[testServer],
testthat[...],
)
::use(
box/main[...],
app
)
test_that("main server works", {
testServer(server, {
expect_equal(output$message, "Hello!")
}) })
- 1
-
box
module importing test package functions - 2
-
Using
shiny::testServer()
andtestthat::test_that()
functions in test.
I’ve included tests for the utility functions and modules in the 21_rhino
branch, but I’ll cover testing with rhino elsewhere.5
26.6 rhino
dependencies
In rhino
apps, dependencies are managed by renv
and the dependencies.R
file. The renv
package is designed to,
“create[s] and manage[s] project-local R libraries, save[s] the state of these libraries to a ‘lockfile’, and later restore[s] the library as required.” 6
The rhino::pkg_install()
helper function updates both the dependencies.R
file and renv
library. Using dependencies.R
, renv
, and box
modules removes the need to manage dependencies in a DESCRIPTION
or NAMESPACE
file.7
Recap
I re-initialize the session on the
21_rhino
branch so I’m not tempted to load, document, install, or test the code using the IDE.↩︎Imported dependencies in
rhino
apps usebox
modules instead of theDESCRIPTION
andNAMESPACE
.↩︎Be sure to read up on testing box modules and
rhino
applications with cypress andshinytest2
.↩︎rhino
recommends placing non-Shiny code in theapp/logic
folder and keeping all Shiny modules and reactive code inapp/view
.↩︎See the Shiny frameworks supplemental website for more information on testing your
rhino
app.↩︎As described in
renv
’s DESCRIPTION file↩︎Be sure to read the
renv
configuration article for a better understanding on how it works with rhino apps.↩︎Check for yourself on cran-downloads↩︎