mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-13 08:57:57 -05:00
370 lines
12 KiB
R
370 lines
12 KiB
R
#' Shiny Developer Mode
|
|
#'
|
|
#' @description `r lifecycle::badge("experimental")`
|
|
#'
|
|
#' Developer Mode enables a number of [options()] to make a developer's life
|
|
#' easier, like enabling non-minified JS and printing messages about
|
|
#' deprecated functions and options.
|
|
#'
|
|
#' Shiny Developer Mode can be enabled by calling `devmode(TRUE)` and disabled
|
|
#' by calling `devmode(FALSE)`.
|
|
#'
|
|
#' Please see the function descriptions for more details.
|
|
#'
|
|
#' @describeIn devmode Function to set two options to enable/disable Shiny
|
|
#' Developer Mode and Developer messages
|
|
#' @param devmode Logical value which should be set to `TRUE` to enable Shiny
|
|
#' Developer Mode
|
|
#' @param verbose Logical value which should be set to `TRUE` display Shiny
|
|
#' Developer messages
|
|
#' @export
|
|
#' @examples
|
|
#' # Enable Shiny Developer mode
|
|
#' devmode()
|
|
#'
|
|
devmode <- function(
|
|
devmode = getOption("shiny.devmode", TRUE),
|
|
verbose = getOption("shiny.devmode.verbose", TRUE)
|
|
) {
|
|
options(
|
|
shiny.devmode = devmode,
|
|
shiny.devmode.verbose = verbose
|
|
)
|
|
}
|
|
|
|
|
|
#' @describeIn devmode Determines if Shiny is in Developer Mode. If the
|
|
#' `getOption("shiny.devmode")` is set to `TRUE` and not in testing inside
|
|
#' `testthat`, then Shiny Developer Mode is enabled.
|
|
#' @section Avoiding direct dependency on shiny:
|
|
#'
|
|
#' The methods explained in this help file act independently from the rest of
|
|
#' Shiny but are included to provide blue prints for your own packages. If
|
|
#' your package already has (or is willing to take) a dependency on Shiny, we
|
|
#' recommend using the exported Shiny methods for consistent behavior. Note
|
|
#' that if you use exported Shiny methods, it will cause the Shiny package to
|
|
#' load. This may be undesirable if your code will be used in (for example) R
|
|
#' Markdown documents that do not have a Shiny runtime (`runtime: shiny`).
|
|
#'
|
|
#' If your package can **not** take a dependency on Shiny, we recommending
|
|
#' re-implementing these two functions:
|
|
#'
|
|
#' \enumerate{
|
|
#' \item `in_devmode()`:
|
|
#'
|
|
#' This function should return `TRUE` if `getOption("shiny.devmode")` is set.
|
|
#' In addition, we strongly recommend that it also checks to make sure
|
|
#' `testthat` is not testing.
|
|
#'
|
|
#' ```r
|
|
#' in_devmode <- function() {
|
|
#' isTRUE(getOption("shiny.devmode", FALSE)) &&
|
|
#' !identical(Sys.getenv("TESTTHAT"), "true")
|
|
#' }
|
|
#' ```
|
|
#'
|
|
#' \item `get_devmode_option(name, default, devmode_default, devmode_message)`:
|
|
#'
|
|
#' This function is similar to `getOption(name, default)`, but when the option
|
|
#' is not set, the default value changes depending on the Dev Mode.
|
|
#' `get_devmode_option()` should be implemented as follows:
|
|
#'
|
|
#' * If not in Dev Mode:
|
|
#' * Return `getOption(name, default)`.
|
|
#' * If in Dev Mode:
|
|
#' * Get the global option `getOption(name)` value.
|
|
#' * If the global option value is set:
|
|
#' * Return the value.
|
|
#' * If the global option value is not set:
|
|
#' * Notify the developer that the Dev Mode default value will be used.
|
|
#' * Return the Dev Mode default value.
|
|
#'
|
|
#' When notifying the developer that the default value has changed, we strongly
|
|
#' recommend displaying a message (`devmode_message`) to `stderr()` once every 8
|
|
#' hours using [rlang::inform()]. This will keep the author up to date as to
|
|
#' which Dev Mode options are being altered. To allow developers a chance to
|
|
#' disable Dev Mode messages, the message should be skipped if
|
|
#' `getOption("shiny.devmode.verbose", TRUE)` is not `TRUE`.
|
|
#'
|
|
#' ```r
|
|
#' get_devmode_option <- function(name, default = NULL, devmode_default, devmode_message) {
|
|
#' if (!in_devmode()) {
|
|
#' # Dev Mode disabled, act like `getOption()`
|
|
#' return(getOption(name, default = default))
|
|
#' }
|
|
#'
|
|
#' # Dev Mode enabled, update the default value for `getOption()`
|
|
#' getOption(name, default = {
|
|
#' # Notify developer
|
|
#' if (
|
|
#' !missing(devmode_message) &&
|
|
#' !is.null(devmode_message) &&
|
|
#' getOption("shiny.devmode.verbose", TRUE)
|
|
#' ) {
|
|
#' rlang::inform(
|
|
#' message = devmode_message,
|
|
#' .frequency = "regularly",
|
|
#' .frequency_id = devmode_message,
|
|
#' .file = stderr()
|
|
#' )
|
|
#' }
|
|
#'
|
|
#' # Return Dev Mode default value `devmode_default`
|
|
#' devmode_default
|
|
#' })
|
|
#' }
|
|
#' ```
|
|
#' }
|
|
#'
|
|
#' The remaining functions in this file are used for author convenience and are
|
|
#' not recommended for all reimplementation situations.
|
|
#' @export
|
|
#' @examples
|
|
#' in_devmode() # TRUE/FALSE?
|
|
#'
|
|
in_devmode <- function() {
|
|
isTRUE(getOption("shiny.devmode", FALSE)) &&
|
|
# !testthat::is_testing()
|
|
!identical(Sys.getenv("TESTTHAT"), "true")
|
|
}
|
|
|
|
in_client_devmode <- function() {
|
|
# Client-side devmode enables client-side only dev features without local
|
|
# devmode. Currently, the main feature is the client-side error console.
|
|
isTRUE(getOption("shiny.client_devmode", FALSE))
|
|
}
|
|
|
|
#' @describeIn devmode Temporarily set Shiny Developer Mode and Developer
|
|
#' message verbosity
|
|
#' @param code Code to execute with the temporary Dev Mode options set
|
|
#' @export
|
|
#' @examples
|
|
#' # Execute code in a temporary shiny dev mode
|
|
#' with_devmode(TRUE, in_devmode()) # TRUE
|
|
#'
|
|
with_devmode <- function(
|
|
devmode,
|
|
code,
|
|
verbose = getOption("shiny.devmode.verbose", TRUE)
|
|
) {
|
|
withr::with_options(
|
|
list(
|
|
shiny.devmode = devmode,
|
|
shiny.devmode.verbose = verbose
|
|
),
|
|
code
|
|
)
|
|
}
|
|
|
|
|
|
#' @describeIn devmode If Shiny Developer Mode and verbosity are enabled,
|
|
#' displays a message once every 8 hrs (by default)
|
|
#' @param message Developer Mode message to be sent to [rlang::inform()]
|
|
#' @param .frequency Frequency of the Developer Mode message used with
|
|
#' [rlang::inform()]. Defaults to once every 8 hours.
|
|
#' @param .frequency_id [rlang::inform()] message identifier. Defaults to
|
|
#' `message`.
|
|
#' @param .file Output connection for [rlang::inform()]. Defaults to [stderr()]
|
|
#' @param ... Parameters passed to [rlang::inform()]
|
|
devmode_inform <- function(
|
|
message,
|
|
.frequency = "regularly",
|
|
.frequency_id = message,
|
|
.file = stderr(),
|
|
...
|
|
) {
|
|
|
|
if (!(
|
|
in_devmode() &&
|
|
isTRUE(getOption("shiny.devmode.verbose", TRUE))
|
|
)) {
|
|
return()
|
|
}
|
|
if (is.null(message)) {
|
|
return()
|
|
}
|
|
|
|
rlang::inform(
|
|
message = paste0("shiny devmode - ", message),
|
|
.frequency = .frequency,
|
|
.frequency_id = .frequency_id,
|
|
.file = .file,
|
|
...
|
|
)
|
|
}
|
|
|
|
|
|
|
|
|
|
registered_devmode_options <- NULL
|
|
on_load({
|
|
registered_devmode_options <- Map$new()
|
|
})
|
|
|
|
#' @describeIn devmode Registers a Shiny Developer Mode option with an updated
|
|
#' value and Developer message. This registration method allows package
|
|
#' authors to write one message in a single location.
|
|
#'
|
|
#' For example, the following Shiny Developer Mode options are registered:
|
|
#'
|
|
#' ```r
|
|
#' # Reload the Shiny app when a sourced R file changes
|
|
#' register_devmode_option(
|
|
#' "shiny.autoreload",
|
|
#' "Turning on shiny autoreload. To disable, call `options(shiny.autoreload = FALSE)`",
|
|
#' devmode_default = TRUE
|
|
#' )
|
|
#'
|
|
#' # Use the unminified Shiny JavaScript file, `shiny.js`
|
|
#' register_devmode_option(
|
|
#' "shiny.minified",
|
|
#' "Using full shiny javascript file. To use the minified version, call `options(shiny.minified = TRUE)`",
|
|
#' devmode_default = FALSE
|
|
#' )
|
|
#'
|
|
#' # Display the full stack trace when errors occur during Shiny app execution
|
|
#' register_devmode_option(
|
|
#' "shiny.fullstacktrace",
|
|
#' "Turning on full stack trace. To disable, call `options(shiny.fullstacktrace = FALSE)`",
|
|
#' devmode_default = TRUE
|
|
#' )
|
|
#' ```
|
|
#'
|
|
#' Other known, non-Shiny Developer Mode options:
|
|
#'
|
|
#' * Sass:
|
|
#' ```r
|
|
#' # Display the full stack trace when errors occur during Shiny app execution
|
|
#' register_devmode_option(
|
|
#' "sass.cache",
|
|
#' "Turning off sass cache. To use default caching, call `options(sass.cache = TRUE)`",
|
|
#' devmode_default = FALSE
|
|
#' )
|
|
#' ```
|
|
#'
|
|
#' @param name Name of option to look for in `options()`
|
|
#' @param default Default value to return if `in_devmode()` returns
|
|
#' `TRUE` and the specified option is not set in [`options()`].
|
|
#' @param devmode_message Message to display once every 8 hours when utilizing
|
|
#' the `devmode_default` value. If `devmode_message` is missing, the
|
|
#' registered `devmode_message` value be used.
|
|
#' @param devmode_default Default value to return if `in_devmode()` returns
|
|
#' `TRUE` and the specified option is not set in [`options()`]. For
|
|
#' `get_devmode_option()`, if `devmode_default` is missing, the
|
|
#' registered `devmode_default` value will be used.
|
|
#' @export
|
|
#' @examples
|
|
#' # Ex: Within shiny, we register the option "shiny.minified"
|
|
#' # to default to `FALSE` when in Dev Mode
|
|
#' \dontrun{register_devmode_option(
|
|
#' "shiny.minified",
|
|
#' devmode_message = paste0(
|
|
#' "Using full shiny javascript file. ",
|
|
#' "To use the minified version, call `options(shiny.minified = TRUE)`"
|
|
#' ),
|
|
#' devmode_default = FALSE
|
|
#' )}
|
|
#'
|
|
register_devmode_option <- function(
|
|
name,
|
|
devmode_message = NULL,
|
|
devmode_default = NULL
|
|
) {
|
|
if (!is.null(devmode_message)) {
|
|
stopifnot(length(devmode_message) == 1 && is.character(devmode_message))
|
|
}
|
|
registered_devmode_options$set(
|
|
name,
|
|
list(devmode_default = devmode_default, devmode_message = devmode_message)
|
|
)
|
|
}
|
|
|
|
|
|
#' @describeIn devmode Provides a consistent way to change the expected
|
|
#' [getOption()] behavior when Developer Mode is enabled. This method is very
|
|
#' similar to [getOption()] where the globally set option takes precedence.
|
|
#' See section "Avoiding direct dependency on shiny" for
|
|
#' `get_devmode_option()` implementation details.
|
|
#'
|
|
#' **Package developers:** Register your Dev Mode option using
|
|
#' `register_devmode_option()` to avoid supplying the same `devmode_default`
|
|
#' and `devmode_message` values throughout your package. (This requires a
|
|
#' \pkg{shiny} dependency.)
|
|
#' @export
|
|
#' @examples
|
|
#' # Used within `shiny::runApp(launch.browser)`
|
|
#' get_devmode_option("shiny.minified", TRUE) # TRUE if Dev mode is off
|
|
#' is_minified <- with_devmode(TRUE, {
|
|
#' get_devmode_option("shiny.minified", TRUE)
|
|
#' })
|
|
#' is_minified # FALSE
|
|
#'
|
|
get_devmode_option <- function(
|
|
name,
|
|
default = NULL,
|
|
devmode_default = missing_arg(),
|
|
devmode_message = missing_arg()
|
|
) {
|
|
getOption(
|
|
name,
|
|
local({
|
|
if (!in_devmode()) {
|
|
# typical case
|
|
return(default)
|
|
}
|
|
|
|
info <- registered_devmode_options$get(name)
|
|
if (is.null(info)) {
|
|
# Not registered,
|
|
# Warn and return default value
|
|
rlang::warn(
|
|
message = paste0(
|
|
"`get_devmode_option(name)` could not find `name = \"", name, "\"`. ",
|
|
"Returning `default` value"
|
|
)
|
|
)
|
|
return(default)
|
|
}
|
|
|
|
# display message
|
|
devmode_inform(
|
|
maybe_missing(
|
|
# use provided `devmode_message` value
|
|
devmode_message,
|
|
# If `devmode_message` is missing, display registered `devmode_message`
|
|
default = info$devmode_message
|
|
)
|
|
)
|
|
|
|
# return value
|
|
maybe_missing(
|
|
# use provided `devmode_default` value
|
|
devmode_default,
|
|
# if `devmode_default` is missing, provide registered `devmode_default`
|
|
default = info$devmode_default
|
|
)
|
|
})
|
|
)
|
|
}
|
|
|
|
|
|
on_load({
|
|
register_devmode_option(
|
|
"shiny.autoreload",
|
|
"Turning on shiny autoreload. To disable, call `options(shiny.autoreload = FALSE)`",
|
|
TRUE
|
|
)
|
|
|
|
register_devmode_option(
|
|
"shiny.minified",
|
|
"Using full shiny javascript file. To use the minified version, call `options(shiny.minified = TRUE)`",
|
|
FALSE
|
|
)
|
|
|
|
register_devmode_option(
|
|
"shiny.fullstacktrace",
|
|
"Turning on full stack trace. To disable, call `options(shiny.fullstacktrace = FALSE)`",
|
|
TRUE
|
|
)
|
|
})
|