Skip to content

Refactor DashR callbacks for increased parity with Dash for Python API #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Feb 20, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
87401c2
:hocho: some callback code, :hammer: for parity with Dash :snake:
Feb 5, 2019
1e37525
refactoring callbacks for parity with Dash for Python
Feb 12, 2019
f59842b
added assert_valid_callbacks
Feb 12, 2019
2a4962e
added call to assert_valid_callbacks
Feb 12, 2019
d0c4182
corrected missing function declaration
Feb 12, 2019
53d37ed
fix silly typo
Feb 12, 2019
5f89e80
Update README.md
rpkyle Feb 12, 2019
525bb76
Update DESCRIPTION
rpkyle Feb 13, 2019
4703fb0
:hammer: unify passing inputs and state
Feb 14, 2019
da17c59
:hocho: event
Feb 14, 2019
c6c4359
fixes for layout rendering
Feb 14, 2019
628e23e
replaced stray user_function with func
Feb 14, 2019
fc74849
replaced one more user_function with func
Feb 14, 2019
63bc0c3
added nested list check for state
Feb 14, 2019
dec6ddf
remove default arguments from id and property in helper fns
Feb 14, 2019
61feba5
validate that params contain only inputs or states
Feb 14, 2019
b88eb58
modified output_value
Feb 18, 2019
1db92b4
removed pointless unlist/cast to list
Feb 18, 2019
7032150
:white_check_mark: added check for input, state ordering
Feb 19, 2019
5951b2b
:bug: fixed check for valid_seq return value
Feb 19, 2019
db27e1a
Update README.md
rpkyle Feb 19, 2019
ebd11b1
Update README.md
rpkyle Feb 19, 2019
86e5268
Update README.md
rpkyle Feb 19, 2019
99c93ad
Update README.md
rpkyle Feb 19, 2019
b54e7aa
Update README.md
rpkyle Feb 19, 2019
1838f7b
Update README.md
rpkyle Feb 19, 2019
53fa11a
adjusting required version for deployment compatibility
Feb 20, 2019
7e51f91
:hocho: key field in dependency obj
Feb 20, 2019
313dff1
:hocho: key field in payload
Feb 20, 2019
f9c9174
:black_circle: wrap layout_render() in invisible
Feb 20, 2019
6cf95ff
add short comment to explain layout_render() behaviour
Feb 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions R/dash.R
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ Dash <- R6::R6Class(

# get the callback associated with this particular output
thisOutput <- with(request$body$output, paste(id, property, sep = "."))
callback <- private$callback_map[[thisOutput]][['user_function']]
callback <- private$callback_map[[thisOutput]][['func']]
if (!length(callback)) stop_report("Couldn't find output component.")
if (!is.function(callback)) {
stop(sprintf("Couldn't find a callback function associated with '%s'", thisOutput))
Expand Down Expand Up @@ -298,6 +298,7 @@ Dash <- R6::R6Class(
},
layout_set = function(...) {
private$layout <- if (is.function(..1)) ..1 else list(...)
private$layout_render()
},

# ------------------------------------------------------------------------
Expand Down Expand Up @@ -344,21 +345,18 @@ Dash <- R6::R6Class(
# ------------------------------------------------------------------------
# callback registration
# ------------------------------------------------------------------------
callback = function(output = NULL, inputs = list(), state = list(), user_function) {
private$layout_render()

assert_valid_callbacks(output, inputs, state, user_function)

# set class attributes for compatibility with original DashR code
for (i in seq_along(inputs)) attr(inputs[[i]], 'class') <- c('dash_dependency', 'input')
for (i in seq_along(state)) attr(state[[i]], 'class') <- c('dash_dependency', 'input')
callback = function(output, params, func) {
assert_valid_callbacks(output, params, func)

inputs <- params[vapply(params, function(x) 'input' %in% attr(x, "class"), FUN.VALUE=logical(1))]
state <- params[vapply(params, function(x) 'state' %in% attr(x, "class"), FUN.VALUE=logical(1))]

# register the callback_map
private$callback_map[[paste(output$id, output$property, sep='.')]] <- list(
output=output,
inputs=inputs,
state=state,
user_function=user_function
func=func
)
},

Expand Down
9 changes: 0 additions & 9 deletions R/dependencies.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,6 @@ state <- function(id = NULL, property = "value") {
)
}

#' @rdname dependencies
#' @export
event <- function(id = NULL, property = "value") {
structure(
dependency(id, property),
class = c("dash_dependency", "event")
)
}

dependency <- function(id = NULL, property = NULL) {
if (is.null(id)) stop("Must specify an id", call. = FALSE)
list(
Expand Down
28 changes: 13 additions & 15 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@ is.fire <- function(x) inherits(x, "Fire")

# dependencies
is.dependency <- function(x) inherits(x, "dash_dependency")
#is.output <- function(x) is.dependency(x) && inherits(x, "output")
is.output <- function(x) return(x)
#is.input <- function(x) is.dependency(x) && inherits(x, "input")
is.input <- function(x) return(x)
#is.state <- function(x) is.dependency(x) && inherits(x, "state")
is.state <- function(x) return(x)
#is.event <- function(x) is.dependency(x) && inherits(x, "event")
is.event <- function(x) return(x)
is.output <- function(x) inherits(x, "output")
is.input <- function(x) inherits(x, "input")
is.state <- function(x) inherits(x, "state")

# components (TODO: this should be exported by dashRtranspile!)
is.component <- function(x) inherits(x, "dash_component")
Expand Down Expand Up @@ -261,35 +256,38 @@ filter_null <- function(x) {
x[!vapply(x, is.null, logical(1))]
}

assert_valid_callbacks <- function(output, inputs, state, user_function) {
assert_valid_callbacks <- function(output, params, func) {
inputs <- params[vapply(params, function(x) 'input' %in% attr(x, "class"), FUN.VALUE=logical(1))]
state <- params[vapply(params, function(x) 'state' %in% attr(x, "class"), FUN.VALUE=logical(1))]

# Assert that the component ID as passed is a string.
if(!(is.character(output$id) & !grepl("^\\s*$", output$id) & !grepl("\\.", output$id))) {
stop(sprintf("Callback IDs must be (non-empty) character strings that do not contain one or more dots/periods. Verify that the component ID is valid."), call. = FALSE)
}

# Assert that user_function is a valid function
if(!(is.function(user_function))) {
if(!(is.function(func))) {
stop(sprintf("The callback method's 'user_function' parameter requires a function as its argument. Verify that user_function is a valid, executable R function."), call. = FALSE)
}

# Check if inputs are a nested list
if(!(any(sapply(inputs, is.list)))) {
stop(sprintf("Callback inputs should be a nested list, in which each element of the sublist represents a component ID and its properties."), call. = FALSE)
}

# Check that input is not NULL
if(is.null(inputs)) {
stop(sprintf("The callback method requires that one or more properly formatted inputs are passed."), call. = FALSE)
}

# Check that outputs are not inputs
# https://github.com/plotly/dash/issues/323
inputs_vs_outputs <- lapply(inputs, function(x) identical(x, output))

if(TRUE %in% inputs_vs_outputs) {
stop(sprintf("Circular input and output arguments were found. Please verify that callback outputs are not also input arguments."), call. = FALSE)
}

# TO DO: check that components contain props
TRUE
}
Expand Down