& R-Hacker at ThinkR, a french company focused on Data Science & R. Hyperactive open source developer. Main developer on the {golem} project. Colin FAY @_ColinFay - https://rtask.thinkr.fr 3 / 130
%>% "PhD in bioinformatics/computational biology (in-progress)" %>% "Venturing into the field of data science, with the little bit I've learnt so far" Colin FAY @_ColinFay - https://rtask.thinkr.fr 6 / 130
Work 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 17 / 130
"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 18 / 130
side: everything you know about package development works with {golem}. Notably: Documentation Testing Colin FAY @_ColinFay - https://rtask.thinkr.fr 19 / 130
NAMESPACE: exported functions + functions from other R/: functions (everything in {golem} is a function) man/: documentation Colin FAY @_ColinFay - https://rtask.thinkr.fr 24 / 130
parameters for {shiny}. #' DO NOT REMOVE. #' @import shiny #' @noRd app_server <- function( input, output, session ) { # List the first level callModules here } server logic can be thought of as a drop in replacement of server.R series of callModule() (if used) Colin FAY @_ColinFay - https://rtask.thinkr.fr 25 / 130
NOT REMOVE. #' @import shiny #' @noRd app_ui <- function(request) { tagList( # Leave this function for adding external resources golem_add_external_resources(), # List the first level UI elements here fluidPage( h1("golex") ) ) } UI counterpart put UI content after the # List the first level UI elements here line Colin FAY @_ColinFay - https://rtask.thinkr.fr 26 / 130
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 27 / 130
@param ... Character vector specifying directory and or file to #' point to inside the current package. #' #' @noRd app_sys <- function(...){ system.file(..., package = "golex") } app_sys("x") will refer to inst/x Colin FAY @_ColinFay - https://rtask.thinkr.fr 28 / 130
yes dev: golem_wd: !expr here::here() Uses the {config} format Can be safely ignored if you don't feel like you need it Colin FAY @_ColinFay - https://rtask.thinkr.fr 30 / 130
├── 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 35 / 130
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 ) 02:00 Colin FAY @_ColinFay - https://rtask.thinkr.fr 36 / 130
) # 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 38 / 130
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 44 / 130
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 45 / 130
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 48 / 130
to put these functions (and how to name them): A module that contains the "About" panel of the App A function that takes a number n, a table, and return the n first rows of the table A function that takes a string, and returns this string as an html tag colored in red. A function that connects to the mongo database 05:00 Colin FAY @_ColinFay - https://rtask.thinkr.fr 49 / 130
R/fct_helpers.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 50 / 130
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 # callModule(mod_my_first_module_server, "my_first_module_ui_1") Colin FAY @_ColinFay - https://rtask.thinkr.fr 59 / 130
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 62 / 130
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 63 / 130
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 64 / 130
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 65 / 130
in mind that depending on another package means that : you will potentially have to recode your package if some breaking change happen in your dependencies If one of your dependencies is removed from CRAN, you will be removed too The more you have deps, the longer it takes to deploy the app Colin FAY @_ColinFay - https://rtask.thinkr.fr 66 / 130
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 68 / 130
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 74 / 130
main technologies that power the web today, along with HTML and JavaScript CSS handles the design, i.e. the visual rendering of the web page: the color of the header, the font, the background, and everything we see Example: try the Web Developer extension of Google Chrome, and remove the CSS from a page In Shiny, there is a default CSS: the one from Bootstrap 3 Colin FAY @_ColinFay - https://rtask.thinkr.fr 76 / 130
syntax is composed of two elements: a selector, and a declaration CSS selector: describes how to identify the HTML tags Declaration: how is the selected style affected h2 { color:red; } Colin FAY @_ColinFay - https://rtask.thinkr.fr 77 / 130
class = "standard">One</h2> name == h2, write the name as-is: h2 id == titleone, prefix the id with #: #titleone class == standard, prefix the class with .: .standard Colin FAY @_ColinFay - https://rtask.thinkr.fr 78 / 130
File automatically linked in `golem_add_external_resources()`. • Go to /Users/colin/golex/inst/app/www/custom.css Colin FAY @_ColinFay - https://rtask.thinkr.fr 82 / 130
At the core of Shiny: building a Shiny app is building a JavaScript app that can talk with an R session Invisible for most Shiny users Built-in in all browser Colin FAY @_ColinFay - https://rtask.thinkr.fr 84 / 130
a tree where the document is the root Contain parents and children Nodes contain attributes DOM Colin FAY @_ColinFay - https://rtask.thinkr.fr 86 / 130
"plouf">Wesh</div> document.querySelector("#pouet") // With the ID document.querySelectorAll(".plouf") // With the class document.getElementById("pouet") // With the ID document.getElementsByName("plop") // With the name attribute document.getElementsByClassName("plouf") // With the class document.getElementsByTagName("div") // With the tag Colin FAY @_ColinFay - https://rtask.thinkr.fr 87 / 130
included in Shiny jQuery makes things like HTML document traversal and manipulation, event handling, animation, and Ajax much simpler with an easy-to-use API that works across a multitude of browsers. Very popular JavaScript library which is designed to manipulate the DOM, its events and its elements jQuery, the most popular JavaScript library ever created, is used in 85.03% of desktop pages and 83.46% of mobile pages. https://almanac.httparchive.org/en/2019/javascript#first-party- vs-third-party Colin FAY @_ColinFay - https://rtask.thinkr.fr 92 / 130
plus some custom Are used inside $() Then call a built-in function $("#plop").hide() Hides the element of class "plop" Colin FAY @_ColinFay - https://rtask.thinkr.fr 93 / 130
the CSS to red $("#id").text( "this" ); Changes the text content to 'this' $("#id").remove(); Removes element from UI Colin FAY @_ColinFay - https://rtask.thinkr.fr 94 / 130
Shiny.addCustomMessageHandler('fun', function(arg) { }) }); Called from R with session$sendCustomMessage("fun", list()) Colin FAY @_ColinFay - https://rtask.thinkr.fr 97 / 130
functions They can be called with golem::invoke_js() golem::invoke_js("showid", ns("plot")) See ?golem::activate_js Colin FAY @_ColinFay - https://rtask.thinkr.fr 101 / 130
Don't trust yourself in 6 months. Be sure to send in production a package with minimum bugs. Good news: you're already writing test, you just didn't know that before. Write tests before it's too late Colin FAY @_ColinFay - https://rtask.thinkr.fr 107 / 130
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 108 / 130
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 109 / 130
& the expected result. If the test is not passed, the function returns an error. If the test passes, the function returns nothing. library(testthat, warn.conflicts = FALSE) expect_equal(10, 10) a <- sample(1:10, 1) b <- sample(1:10, 1) expect_equal(a+b, 200) Error: a + b not equal to 200. 1/1 mismatches [1] 5 - 200 == -195 Colin FAY @_ColinFay - https://rtask.thinkr.fr 111 / 130
the command line (i.e. in the terminal): R CMD check. Or simply the devtools::check() function in your R session. More tests are performed with check than with devtools::test(), which "only" performs the tests in the test folder. This command runs around 50 different tests. Is performed when you click the "Check" button on the Build tab of RStudio. Colin FAY @_ColinFay - https://rtask.thinkr.fr 115 / 130
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 126 / 130