a french company focused on Data Science & R. Hyperactive open source developer, lead developer of the {golem} project. https://thinkr.fr https://rtask.thinkr.fr https://twitter.com/_colinfay https://github.com/colinfay https://colinfay.me Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 4 / 84
with reliable tools Gain time developing Simplify deployment Standardize team work About {golem} at ThinkR: Internal need, used on a daily basis Need reliable tooling for deploying to our clients' environments Build and share good practices globally Promote R & Shiny in production Why {golem}? Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 11 / 84
"prod-ready" Shiny App? Has meta data (DESCRIPTION) Divided in functions (R/) Tested (tests/) With dependencies (NAMESPACE) Documented (man/ & vignettes) Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 12 / 84
side: everything you know about package development works with {golem}. Notably: Documentation Testing Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 13 / 84
NAMESPACE: exported functions + functions from other R/: functions (everything in {golem} is a function) man/: documentation Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 18 / 84
parameters for {shiny}. #' DO NOT REMOVE. #' @import shiny #' @noRd app_server <- function( input, output, session ) { # Your application server logic } server logic can be thought of as a drop in replacement of server.R series of callModule() / module_server() (if used) Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 19 / 84
NOT REMOVE. #' @import shiny #' @noRd app_ui <- function(request) { tagList( # Leave this function for adding external resources golem_add_external_resources(), # Your application UI logic fluidPage( h1("golex") ) ) } UI counterpart put UI content after the # Your application UI logic line Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 20 / 84
bundle_resources( path = app_sys('app/www'), app_title = 'golex' ) # Add here other external resources # for example, you can add shinyalert::useShinyalert() ) } Used to add external resources Will integrate CSS and JS in the app Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 21 / 84
NOTE: If you manually change your package name in the DESCRIPTION, #' don't forget to change it here too, and in the config file. #' For a safer name change mechanism, use the `golem::set_golem_name()` function. #' #' @param ... character vectors, specifying subdirectory and file(s) #' within your package. The default, none, returns the root of the app. #' #' @noRd app_sys <- function(...){ system.file(..., package = "golex") } app_sys("x") will refer to inst/x Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 22 / 84
= TRUE ){ config::get( value = value, config = config, # Modify this if your config file is somewhere else: file = app_sys("golem-config.yml"), use_parent = use_parent ) } Retrieves elements from inst/golem-config.yml Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 23 / 84
├── 03_deploy.R └── run_dev.R Four files that bundle the golem workflow: 01_start.R: run once at the beginning of the project 02_dev.R: day to day development 03_deploy.R: to use before sending to prod run_dev.R: to relaunch your app during development Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 29 / 84
The Name of the package containing the App pkg_title = "PKG_TITLE", # The Title of the package containing the App pkg_description = "PKG_DESC.", # The Description of the package containing the App author_first_name = "AUTHOR_FIRST", # Your First Name author_last_name = "AUTHOR_LAST", # Your Last Name author_email = "[email protected]", # Your Email repo_url = NULL # The (optional) URL of the GitHub Repo ) 04:00 Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 30 / 84
yaml file golem::use_recommended_tests() Sets common dependencies Use golem::set_golem_name() to globally change your app name Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 31 / 84
) # You can set another license here usethis::use_readme_rmd( open = FALSE ) usethis::use_code_of_conduct() usethis::use_lifecycle_badge( "Experimental" ) usethis::use_news_md( open = FALSE ) usethis::use_git() Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 32 / 84
make your Shiny app interactive Business logic: core algorithms and functions that make your application specific to your area of work Keep them separated! Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 36 / 84
business logic functions mod_*: file with ONE module (ui + server) utils_*: cross module utilitarian functions *_ui_* / *_server_*: relates to UI and SERVER Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 37 / 84
Easier to work on functions when they are not inside the app (you don't need to relaunch the app every time) You can test your business logic with classical package testing tools You can use the business logic functions outside the app Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 40 / 84
at R/fct_helpers.R • Go to R/fct_helpers.R ✓ File created at R/utils_data_ui.R • Go to R/utils_data_ui.R Creates fct_* and utils_* files golem::add_module( name = "my_first_module") ✓ File created at R/mod_my_first_module.R • Go to R/mod_my_first_module.R Builds a skeleton for a Shiny Module Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 41 / 84
function(id){ moduleServer( id, function(input, output, session){ ns <- session$ns }) } ## To be copied in the UI # mod_my_first_module_ui("my_first_module_ui_1") ## To be copied in the server # mod_my_first_module_server("my_first_module_ui_1") Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 50 / 84
of your package ⚠ NEVER EDIT BY HAND ⚠ Describes how your package interacts with R, and with other packages Lists functions that are exported (from your app) and imported (from other packages) Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 54 / 84
from {shiny}) The DESCRIPTION file contains the package dependencies Are added to the DESCRIPTION with: usethis::use_package("attempt") Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 55 / 84
top of each functions that specify what deps are imported Either with @import (a whole package) and @importFrom (a specific function). golem built modules, by default, import elements from {shiny}: #' @importFrom shiny NS tagList Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 56 / 84
stats na.omit mean_no_na <- function(x){ x <- x %>% na.omit() sum(x)/length(x) } You can use import or importFrom. The better is to use importFrom, for preventing namespace conflict. Add to EACH function. It will take some time, but it's better on the long run. Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 57 / 84
Call usethis::use_data_raw from dev/02_dev.R ## Add internal datasets ---- ## If you have data in your package usethis::use_data_raw( name = "dataset" ) Creates data-raw/dataset.R Inside this file: ## code to prepare `dataset` dataset goes here usethis::use_data("dataset") Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 59 / 84
frequently, it's better to use a database as a backend. Choice Update Size Package data Never to very rare Low to medium Reading files Uploaded by Users Preferably low External DataBase Never to Streaming Low to Big Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 61 / 84
every .css and .js file contained in inst/app/www golem_add_external_resources <- function(){ add_resource_path( 'www', app_sys('app/www') ) tags$head( favicon(), bundle_resources( path = app_sys('app/www'), app_title = 'golex' ) # Add here other external resources # for example, you can add shinyalert::useShinyalert() ) } Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 64 / 84
One fluidRow Inside this fluidRow, insert an h3 Use the CSS located online ☝ The link to the CSS will be pasted in the conference chat ☝ 05:00 Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 66 / 84
more important to test that the algorithm is still accurate than testing the UI As we've separated businness logic from application logic, we use package development testing tools We'll use the {testthat} Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 71 / 84
for tests golem::use_recommended_tests() Recommended tests 02_dev.R ## Tests ---- ## Add one line by test you want to create usethis::use_test( "app" ) Add custom test Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 72 / 84
should be pain-free Bugs should be detected quickly New collaborators should be able to integrate a team smoothly Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 73 / 84
tests Serving application cost money (corollary) You shouldn't spend 1 million bucks on AWS (Jeff Bezos is rich enough) You shouldn't DoS your own server Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 74 / 84
front-end/design What - User Interface Your time is limited, so if you have to choose don't focus too much on testint the UI only Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 75 / 84
your users rely on What - Business logic Try to test business logic as extensively as possible Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 76 / 84
estimate will lead to slow application performances If the app needs to scale, it's crucial to kow it upfront What - Application Load Poor app performances lead to bad UX, and potentially cost Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 77 / 84
Cli to record and replay load tests {dockerstats} : get Docker stats inside R {crrry} + {dockerstats} : replay session and watch the Docker stats Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 80 / 84
RStudio related platforms golem::add_rstudioconnect_file() golem::add_shinyappsio_file() golem::add_shinyserver_file() ## Docker ---- ## If you want to deploy via a generic Dockerfile golem::add_dockerfile() ## If you want to deploy to ShinyProxy golem::add_dockerfile_shinyproxy() ## If you want to deploy to Heroku golem::add_dockerfile_heroku() Send to prod Once everything is tested, you can send to prod using {golem} Deploy on a server, or build as a tar.gz with pkgbuild::build() Colin FAY (@_ColinFay) - https://rtask.thinkr.fr 82 / 84