install.packages("golem")
library(golem)
23 golem
This chapter walks through building a version of the sap
with the golem
framework. The resulting app-package (gap
) is in the 18_golem
branch.
The version/description of golem
used in this Shiny app-package is:
Package | Version | Title | Description |
---|---|---|---|
golem | 0.5.1 | A Framework for Robust Shiny Applications | An opinionated framework for building a production-ready 'Shiny' application. This package contains a series of tools for building a robust 'Shiny' application from start to finish. |
After checking out the 18_golem
branch, be sure to load, document, and install the application.
Ctrl/Cmd + Shift + L / D / B
23.1 gap
(a golem app-package)
gap
exports the movies
data and the standalone app function, run_app()
.
library(gap)
::run_app() gap
Launch app with the shinypak
package:
launch('18_golem')
In the sections below, I’ll note various features and workflows that differ from standard package (or app-package) development.
23.2 dev
scripts
New golem
apps have a dev/
folder with scripts for setting up your application, development, and launching/deploying.
dev
├── 01_start.R
├── 02_dev.R
└── 03_deploy.R
The package website has each script available in vignettes: 01_start, 02_dev, 03_deploy
23.3 Set up
The dev/01_start.R
file opens when the new golem
app-package launches. Following the steps in 01_start.R
sets up the following files, folders, and options in your golem
app-package:
DESCRIPTION
:golem
has a custom function for entering many of the fields we covered in the Packages chapter 1golem
has functions that automatically set multiple options in thegolem-config.yml
2 and package dependencies 3usethis
functions are called for aLICENSE
4, creating (and building5) aREADME
6 aCODE_OF_CONDUCT.md
7, adding a Lifecycle badge8, and theNEWS.md
file.9 Many of these functions and files are covered in the Whole App Game chapter.If you aren’t using Git, there’s an option to initiate a Git repo 10
The testing infrastructure is set up using the custom
golem::use_recommended_tests()
function, which creates the necessarytestthat
folders we covered in the Test suite chapter 11, but also adds a collection of boilerplate tests.golem
apps also have custom functions for creating a series of UI 12 and server 13 utility functions (and their accompanying tests).
23.4 Development
After setting up the golem
app-package with dev/01_start.R
, the dev/02_dev.R
file opens and contains the following functions/options for developing your application.
- Package dependencies: imports are managed with the
attachment
package14
- Code files: new code files in
golem
apps can be created with a variety of helper functions.
- Modules:
add_module()
adds a.R
file with amod_
prefix (an optional test can be included with thewith_test = TRUE
argument) - Utility functions: utility functions can be added with
add_utils()
oradd_fct()
(also include thewith_test
option for tests).golem_utils_*
files contain commonly used UI and server functions. - The primary app UI and server functions are stored in
R/app_ui.R
andR/app_server.R
. - The standalone app function is stored in
R/run_app.R.
R/_disable_autoload.R
disablesshiny::loadSupport()
(an option we covered in the Launch chapter)R/app_config.R
contains configuration functions:
app_sys()
is a wrapper forsystem.file()
get_golem_config()
reads environment variables (i.e.,GOLEM_CONFIG_ACTIVE
) and the contents ofinst/golem-config.yml
:
- Modules:
R
├── _disable_autoload.R
├── app_config.R
├── app_server.R
├── app_ui.R
├── data.R
├── golem_utils_server.R
├── golem_utils_ui.R
├── mod_scatter_display.R
├── mod_var_input.R
├── utils_mod_scatter_display.R
├── utils_tests.R
└── run_app.R
1 directory, 11 files
- 1
-
Turn off
loadSupport()
- 2
- Configure app functions
- 3
-
App UI and server functions
- 4
-
Data documentation
- 5
-
golem
utility functions
- 6
-
Scatter plot module
- 7
- Variable input module
- 8
-
Scatter plot utility function (
scatter_plot()
) - 9
-
Test utility function (
test_logger()
) - 10
- Standalone app function
- External files: adding external files is handled with
golem_add_external_resources()
, which uses the same methods we covered in the Resources chapter.15
WORDLIST
: includes the word ‘golem
’ and is an artifact fromspellcheck
argument inuse_recommended_tests()
. 16app/
contains the application files. 17extdata/
contains the external data files. 18golem-config.yml
is used to set various configuration options. 19
inst
├── WORDLIST
├── app
│ └── www
│ ├── favicon.ico
│ ├── golem-hex.png
│ └── shiny.png
├── extdata
│ ├── movies.RData
│ └── tidy_movies.fst
└── golem-config.yml
4 directories, 7 files
- Data: the data in
golem
app-packages function like the data folder and files in a standard R package we covered in the Data chapter.
data/
: contains themovies.rda
file used in the applicationdata-raw/
:movies.R
reads indata-raw/movies.RData
and createsdata/movies.rda
tidy_movies.R
reads in theggplot2movies::movies
data and creates theinst/extdata/tidy_movies.fst
data.
data
└── movies.rda
1 directory, 1 file
data-raw
├── movies.R
├── movies.RData
└── tidy_movies.R
1 directory, 2 files
- Documentation: the
roxygen2
documentation ingolem
app-package files comes with boilerplate tags and descriptions similar to those covered in the Documentation chapter.
man/
: By default, modules created withadd_module()
aren’t exported 20vignettes/
: Package vignettes ingolem
app-packages operate like vignettes in standard R packages.
man
├── app_server.Rd
├── app_ui.Rd
├── golem_add_external_resources.Rd
├── mod_scatter_display_server.Rd
├── mod_scatter_display_ui.Rd
├── mod_plot_ui.Rd
├── mod_var_input_server.Rd
├── mod_var_input_ui.Rd
├── movies.Rd
├── run_app.Rd
├── scatter_plot.Rd
└── test_logger.Rd
1 directory, 12 files
- 1
-
Primary UI and server functions (‘pre-packaged’ in
golem
apps) - 2
-
External resources utility function (‘pre-packaged’ in
golem
apps)
- 3
- Modules
- 4
- Data documentation
- 5
-
The ‘pre-packaged’ standalone app function (only export from
golem
apps)
- 6
-
Plot utility function (exported from
gap
)
- 7
- Test utility function
vignettes/
└── tests_and_coverage.Rmd
1 directory, 1 file
23.5 Tests
golem
applications provide a lot of boilerplate tests.
tests
├── README.md
├── spelling.R
├── testthat
│ ├── _snaps
│ ├── fixtures
│ │ ├── make-tidy_ggp2_movies.R
│ │ └── tidy_ggp2_movies.rds
│ ├── helper.R
│ ├── setup-shinytest2.R
│ ├── test-app-feature-01.R
│ ├── test-golem-recommended.R
│ ├── test-golem_utils_server.R
│ ├── test-golem_utils_ui.R
│ ├── test-mod_scatter_display.R
│ ├── test-mod_var_input.R
│ ├── test-shinytest2.R
│ └── test-utils_mod_scatter_display.R
└── testthat.R
4 directories, 15 files
- 1
-
Created from
covrpage
package
- 2
-
Created from spelling package
- 3
-
Test fixtures
- 4
-
Test helpers
- 5
-
Setting up
shinytest2
- 6
-
shinytest2
feature test
- 7
-
Created with:
golem::use_recommended_tests()
- 8
-
Created with:
golem::use_utils_ui(with_test = TRUE)
- 9
-
Created with:
golem::use_utils_server(with_test = TRUE)
- 10
-
Created with:
golem::add_module(name = 'scatter_display',
with_test = TRUE)
- 11
-
Created with:
golem::add_module(name = 'var_input',
with_test = TRUE)
- 12
-
Test recording from
shinytest2::record_test()
- 13
-
Utility function test (
scatter_plot()
)
- Tests: The testing framework for
golem
app-packages is set up withgolem::use_recommended_tests()
in thedev/01_start.R
script.21
- A
tests/README.md
file is created by thecovrpage
package 22 tests/spelling.R
adds functionality from thespelling
package 23testthat
: The twogolem
utility function files (golem_utils_server.R
, andgolem_utils_ui.R
) have accompanying tests files.
- The
with_test
argument creates test files for modules and utility functions. 24
- The
- A
23.5.1 Unit tests
I’ve converted the tests from the previous chapters for the modules and utility functions in gap
:
test-golem-recommended.R
contains tests for the functions included in your new golem app (app_ui()
,app_sys()
, etc.)test-golem_utils_server.R
contains utility functions that might be useful in theserver
test-golem_utils_ui.R
contains utility functions that might be useful in theui
The tests for
scatter_plot()
is in thetest-utils_mod_scatter_display.R
file.
23.5.2 Module tests
- The communication between
mod_var_input_server()
andmod_scatter_display_server()
are intest-mod_var_input.R
andtest-mod_scatter_display.R
23.5.3 System tests
The two system tests are in test-shinytest2.R
and test-app-feature-01.R
:
test-shinytest2.R
is the initial resulting test fromshinytest2::record_test()
covered in System teststest-app-feature-01.R
contains feature tests
23.6 Deployment
When you’re ready to deploy your golem
app, the dev/03_deploy.R
file contains a ‘pre deployment checklist’ with multiple options for deploying your application.
23.6.1 Posit platforms
23.6.2 Docker
golem
has multiple options for creating Docker files:
golem::add_dockerfile()
adds the followingDockerfile
:show/hide Dockerfile
FROM rocker/verse:4.3.2 RUN apt-get update && apt-get install -y libcurl4-openssl-dev libicu-dev libssl-dev libxml2-dev make pandoc zlib1g-dev && rm -rf /var/lib/apt/lists/* RUN mkdir -p /usr/local/lib/R/etc/ /usr/lib/R/etc/ RUN echo "options(repos = c(CRAN = 'https://cran.rstudio.com/'), download.file.method = 'libcurl', Ncpus = 4)" | tee /usr/local/lib/R/etc/Rprofile.site | tee /usr/lib/R/etc/Rprofile.site RUN R -e 'install.packages("remotes")' RUN Rscript -e 'remotes::install_version("glue",upgrade="never", version = "1.6.2")' RUN Rscript -e 'remotes::install_version("rlang",upgrade="never", version = "1.1.2")' RUN Rscript -e 'remotes::install_version("stringr",upgrade="never", version = "1.5.1")' RUN Rscript -e 'remotes::install_version("knitr",upgrade="never", version = "1.45")' RUN Rscript -e 'remotes::install_version("waldo",upgrade="never", version = "0.5.2")' RUN Rscript -e 'remotes::install_version("shiny",upgrade="never", version = "1.8.0")' RUN Rscript -e 'remotes::install_version("rmarkdown",upgrade="never", version = "2.25")' RUN Rscript -e 'remotes::install_version("config",upgrade="never", version = "0.3.2")' RUN Rscript -e 'remotes::install_version("spelling",upgrade="never", version = "2.2.1")' RUN Rscript -e 'remotes::install_version("shinytest2",upgrade="never", version = "0.3.1")' RUN Rscript -e 'remotes::install_version("ggplot2movies",upgrade="never", version = "0.0.1")' RUN Rscript -e 'remotes::install_version("logger",upgrade="never", version = "0.2.2")' RUN Rscript -e 'remotes::install_version("golem",upgrade="never", version = "0.4.1")' RUN Rscript -e 'remotes::install_version("ggplot2",upgrade="never", version = "3.4.4")' RUN Rscript -e 'remotes::install_github("rstudio/htmltools@a8a3559edbfd9dda78418251e69273fa9dfeb9bc")' RUN Rscript -e 'remotes::install_github("r-lib/testthat@fe50a222c62cc8733b397690caf3b2a95856f902")' RUN mkdir /build_zone ADD . /build_zone WORKDIR /build_zone RUN R -e 'remotes::install_local(upgrade="never")' RUN rm -rf /build_zone EXPOSE 80 CMD R -e "options('shiny.port'=80,shiny.host='0.0.0.0');library(gap);gap::run_app()"
golem::add_dockerfile_with_renv()
creates atmp/deploy
folder and adds the following files:
deploy/
├── Dockerfile
├── Dockerfile_base
├── README
├── gap_0.0.0.9000.tar.gz
└── renv.lock.prod
README
docker build -f Dockerfile_base --progress=plain -t gap_base . docker build -f Dockerfile --progress=plain -t gap:latest . docker run -p 80:80 gap:latest # then go to 127.0.0.1:80
Dockerfile
show/hide Dockerfile
FROM gap_base COPY renv.lock.prod renv.lock RUN R -e 'renv::restore()' COPY gap_*.tar.gz /app.tar.gz RUN R -e 'remotes::install_local("/app.tar.gz",upgrade="never")' RUN rm /app.tar.gz EXPOSE 80 CMD R -e "options('shiny.port'=80,shiny.host='0.0.0.0');library(gap);gap::run_app()"
Dockerfile_base
show/hide Dockerfile_base
FROM rocker/verse:4.3.2 RUN apt-get update -y && apt-get install -y make zlib1g-dev git libicu-dev && rm -rf /var/lib/apt/lists/* RUN mkdir -p /usr/local/lib/R/etc/ /usr/lib/R/etc/ RUN echo "options(renv.config.pak.enabled = FALSE, repos = c(CRAN = 'https://cran.rstudio.com/'), download.file.method = 'libcurl', Ncpus = 4)" | tee /usr/local/lib/R/etc/Rprofile.site | tee /usr/lib/R/etc/Rprofile.site RUN R -e 'install.packages("remotes")' RUN R -e 'remotes::install_version("renv", version = "1.0.3")' COPY renv.lock.prod renv.lock RUN R -e 'renv::restore()'
gap_0.0.0.9000.tar.gz
is a compressed version of our app-package to deploy in the Docker container.renv.lock.prod
is a JSON file with a list of packages used in our app-package.
You can read more details about deploying with Docker on the Shiny Frameworks supplemental website.
23.7 Summary of golem
features
golem
s helper functions and dev
scripts make application development fast–I was able to create gap
quickly, and all of the supporting packages (covrpage
, attachment
, spelling
) make the development process faster/easier:
- The two modules (
mod_plot
andmod_var
) are easily created withadd_module()
, the utility function withadd_utils()
- We can easily add the modules to the
app_ui()
andapp_server()
- App images are moved into
inst/app/www/
- The movies data was added to
inst/extdata/
, then read intodata/
folder with thedata-raw/movies.R
file. - For documentation, the
attachment::att_amend_desc()
function quickly captures any dependencies
- Finally, I loaded, documented, and installed the
gap
package and ran the application withgap::run_app()
If you’ve followed along with the preceding chapters, the golem
framework will be familiar. In essence, golem
takes many of the package development steps we’ve covered and bundles them into wrapper functions (i.e., add_module()
is similar to running usethis::use_r()
and usethis::use_test()
, then adding an roxygen2
skeleton).
23.8 Dependencies
It’s also worth noting that using the golem
framework adds golem
as a dependency:
# in the 18_golem branch of sap
::local_deps_explain(deps = 'golem', root = ".") pak
gap -> golem
23.8.1 gap
dependencies
See the 09d_inst-prod
branch of sap
.
For comparison, this is the sap
dependency tree (note that using devtools
/usethis
doesn’t make our app-package depend on these packages).
# in the 09d_inst-prod branch of sap
::local_deps_explain(deps = 'devtools', root = ".") pak
x devtools
# in the 09d_inst-prod branch of sap
::local_deps_explain(deps = 'usethis', root = ".") pak
x usethis
Recap
Fields are filled with
golem::fill_desc()
↩︎Options are set with with
golem::set_golem_options()
↩︎Dependencies are installed with
golem::install_dev_deps()
↩︎Created using
usethis::use_mit_license()
↩︎Built using
devtools::build_readme()
↩︎Created using
usethis::use_readme_rmd()
↩︎Created using
usethis::use_code_of_conduct()
↩︎Created using
usethis::use_lifecycle_badge()
↩︎Created using
usethis::use_news_md()
↩︎Initialize Git using
usethis::use_git()
↩︎The
tests/
folder andtestthat
files are included withgolem::use_recommended_tests()
↩︎Create UI utility functions using
golem::use_utils_ui()
↩︎Create server utility functions using
golem::use_utils_server()
↩︎attachment::att_amend_desc()
parses the code underR/
and make sure theDESCRIPTION
file is up-to-date↩︎golem_add_external_resources()
is a wrapper forgolem::add_resource_path()
, which is a wrapper forshiny::addResourcePath()
(andapp_sys()
is a wrapper forsystem.file()
).↩︎The
use_recommended_tests()
is run in thedev/01_start.R
file and ifspellcheck
isTRUE
, creates thetests/spelling.R
file and theinst/WORDLIST
file.↩︎the
app/
folder is used to add external resources to the application (similar to the previous versions ofsap
).↩︎This contains the
RData
file for the original movies data and the exportedtidy_movies.fst
file.↩︎golem
apps use agolem-config.yml
file for setting various options. These are initially set withset_golem_options()
(and based on theconfig
package)↩︎The
noRd
tag is added to module files created withadd_module()
, but you can export these functions by setting theexport
argument toTRUE
.@importFrom
is used to importNS()
andtagList()
.↩︎test-golem-recommended.R
contains the recommended tests forapp_ui()
,app_server()
,app_sys()
, andgolem-config.yml
↩︎The
covrpage
package is not on CRAN, but the development version always seems to work. Create thetests/README.md
file withcovrpage::covrpage()
.↩︎The
spelling
package will spell check vignettes, packages, etc.↩︎with_test = TRUE)
adds tests in thedev/01_start.R
script. Code files created withgolem::add_module()
,golem::add_utils()
, andgolem::add_fct()
will also include a test file ifwith_test
is set toTRUE
.↩︎This also includes a call to
rhub::check_for_cran()
, which may or may not be of concern for your application.↩︎These functions will create and
app.R
file to launch and deploy your application.↩︎Includes boilerplate for
appName
,appTitle
,appFiles
, etc.↩︎