Add req function for validating required inputs/values

This commit is contained in:
Joe Cheng
2015-12-16 10:04:05 -08:00
parent 3a0ce86f51
commit eee6f4ed81
6 changed files with 191 additions and 0 deletions

View File

@@ -153,6 +153,7 @@ export(renderTable)
export(renderText)
export(renderUI)
export(repeatable)
export(req)
export(runApp)
export(runExample)
export(runGist)

5
NEWS
View File

@@ -50,6 +50,11 @@ shiny 0.12.2.9000
* Fixed #1018: the selected value of a selectize input is guaranteed to be
selected in server-side mode.
* Added `req` function, which provides a simple way to prevent a reactive,
observer, or output from executing until all required inputs and values are
available. (Similar functionality has been available for a while using
validate/need, but req provides a much simpler and more direct interface.)
shiny 0.12.2
--------------------------------------------------------------------------------

View File

@@ -980,6 +980,92 @@ need <- function(expr, message = paste(label, "must be provided"), label) {
return(invisible(NULL))
}
#' Check for required values
#'
#' Ensure that values are available ("truthy"--see Details) before proceeding
#' with a calculation or action. If any of the given values is not truthy, the
#' operation is stopped by raising a "silent" exception (not logged by Shiny,
#' nor displayed in the Shiny app's UI).
#'
#' The \code{req} function was designed to be used in one of two ways. The first
#' is to call it like a statement (ignoring its return value) before attempting
#' operations using the required values:
#'
#' \preformatted{rv <- reactiveValues(state = FALSE)
#' r <- reactive({
#' req(input$a, input$b, rv$state)
#' # Code that uses input$a, input$b, and/or rv$state...
#' })}
#'
#' In this example, if \code{r()} is called and any of \code{input$a},
#' \code{input$b}, and \code{rv$state} are \code{NULL}, \code{FALSE}, \code{""},
#' etc., then the \code{req} call will trigger an error that propagates all the
#' way up to whatever render block or observer is executing.
#'
#' The second is to use it to wrap an expression that must be truthy:
#'
#' \preformatted{output$plot <- renderPlot({
#' if (req(input$plotType) == "histogram") {
#' hist(dataset())
#' } else if (input$plotType == "scatter") {
#' qplot(dataset(), aes(x = x, y = y))
#' }
#' })}
#'
#' In this example, \code{req(input$plotType)} first checks that
#' \code{input$plotType} is truthy, and if so, returns it. This is a convenient
#' way to check for a value "inline" with its first use.
#'
#' \strong{Truthy and falsy values}
#'
#' The terms "truthy" and "falsy" generally indicate whether a value, when
#' coerced to a \code{\link{logical}}, is \code{TRUE} or \code{FALSE}. We use
#' the term a little loosely here; our usage tries to match the intuitive
#' notions of "Is this value missing or available?", or "Has the user provided
#' an answer?", or in the case of action buttons, "Has the button been
#' clicked?".
#'
#' For example, a \code{textInput} that has not been filled out by the user has
#' a value of \code{""}, so that is considered a falsy value.
#'
#' To be precise, \code{req} considers a value truthy \emph{unless} it is one
#' of:
#'
#' \itemize{
#' \item{\code{FALSE}}
#' \item{\code{NULL}}
#' \item{\code{""}}
#' \item{An empty atomic vector}
#' \item{An atomic vector that contains only missing values}
#' \item{A logical vector that contains all \code{FALSE} or missing values}
#' \item{An object of class \code{"try-error"}}
#' \item{A value that represents an unclicked \code{\link{actionButton}}}
#' }
#'
#' Note in particular that the value \code{0} is considered truthy, even though
#' \code{as.logical(0)} is \code{FALSE}.
#'
#' If the built-in rules for truthiness do not match your requirements, you can
#' always work around them. Since \code{FALSE} is falsy, you can simply provide
#' the results of your own checks to \code{req}:
#'
#' \code{req(input$a != 0)}
#'
#' @param ... Values to check for truthiness.
#' @return The first value that was passed in.
#'
#' @export
req <- function(...) {
for (item in list(...)) {
if (!isTruthy(item))
stopWithCondition("validation", "")
}
if (length(list(...)) > 0)
..1
else
invisible()
}
isTruthy <- function(x) {
if (inherits(x, 'try-error'))
return(FALSE)

View File

@@ -147,6 +147,7 @@ sd_section("Extending Shiny",
sd_section("Utility functions",
"Miscellaneous utilities that may be useful to advanced users or when extending Shiny.",
c(
"req",
"validate",
"session",
"exprToFunction",

View File

@@ -89,6 +89,18 @@ test_that("need() works as expected", {
expect_null(need(c(FALSE, FALSE, TRUE), FALSE))
})
test_that("req works", {
expect_error(req(TRUE, FALSE))
expect_error(req(TRUE, stop("boom")))
expect_equivalent(req(1, TRUE), 1)
# All req arguments are evaluated before any are tested for truthiness. This
# isn't necessary a good property, but let's at least document it with a test.
value <- 0
expect_error(req(NULL, value <- 1))
expect_equal(value, 1)
})
test_that("anyUnnamed works as expected", {
expect_false(anyUnnamed(list()))
expect_true(anyUnnamed(list(1,2,3)))

86
man/req.Rd Normal file
View File

@@ -0,0 +1,86 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/utils.R
\name{req}
\alias{req}
\title{Check for required values}
\usage{
req(...)
}
\arguments{
\item{...}{Values to check for truthiness.}
}
\value{
The first value that was passed in.
}
\description{
Ensure that values are available ("truthy"--see Details) before proceeding
with a calculation or action. If any of the given values is not truthy, the
operation is stopped by raising a "silent" exception (not logged by Shiny,
nor displayed in the Shiny app's UI).
}
\details{
The \code{req} function was designed to be used in one of two ways. The first
is to call it like a statement (ignoring its return value) before attempting
operations using the required values:
\preformatted{rv <- reactiveValues(state = FALSE)
r <- reactive({
req(input$a, input$b, rv$state)
# Code that uses input$a, input$b, and/or rv$state...
})}
In this example, if \code{r()} is called and any of \code{input$a},
\code{input$b}, and \code{rv$state} are \code{NULL}, \code{FALSE}, \code{""},
etc., then the \code{req} call will trigger an error that propagates all the
way up to whatever render block or observer is executing.
The second is to use it to wrap an expression that must be truthy:
\preformatted{output$plot <- renderPlot({
if (req(input$plotType) == "histogram") {
hist(dataset())
} else if (input$plotType == "scatter") {
qplot(dataset(), aes(x = x, y = y))
}
})}
In this example, \code{req(input$plotType)} first checks that
\code{input$plotType} is truthy, and if so, returns it. This is a convenient
way to check for a value "inline" with its first use.
\strong{Truthy and falsy values}
The terms "truthy" and "falsy" generally indicate whether a value, when
coerced to a \code{\link{logical}}, is \code{TRUE} or \code{FALSE}. We use
the term a little loosely here; our usage tries to match the intuitive
notions of "Is this value missing or available?", or "Has the user provided
an answer?", or in the case of action buttons, "Has the button been
clicked?".
For example, a \code{textInput} that has not been filled out by the user has
a value of \code{""}, so that is considered a falsy value.
To be precise, \code{req} considers a value truthy \emph{unless} it is one
of:
\itemize{
\item{\code{FALSE}}
\item{\code{NULL}}
\item{\code{""}}
\item{An empty atomic vector}
\item{An atomic vector that contains only missing values}
\item{A logical vector that contains all \code{FALSE} or missing values}
\item{An object of class \code{"try-error"}}
\item{A value that represents an unclicked \code{\link{actionButton}}}
}
Note in particular that the value \code{0} is considered truthy, even though
\code{as.logical(0)} is \code{FALSE}.
If the built-in rules for truthiness do not match your requirements, you can
always work around them. Since \code{FALSE} is falsy, you can simply provide
the results of your own checks to \code{req}:
\code{req(input$a != 0)}
}