Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff9cc4d
Add support for 'subplots' from plot_ly and ggplotly in DashR (#84)
rpkyle Apr 18, 2019
fcb0972
Remove run_heroku() and inspect environment vars for setting host and…
rpkyle Apr 18, 2019
ca533f2
:bell: added warning when file goes unmatched (#81)
rpkyle Apr 22, 2019
689e309
:sparkles: initial support for stack tracing
Apr 23, 2019
cfe9c49
:truck: add dependency on crayon :package:
Apr 27, 2019
897df31
:scissors: trim call stack from top also
Apr 27, 2019
0ad3b38
renamed stack trace fn
Apr 27, 2019
0d526b4
:sparkles: capture errors as warnings in debug mode
Apr 27, 2019
6291553
Merge branch '0.0.7-dev' into 0.0.7-debug
rpkyle Apr 28, 2019
d84b964
:sparkles: initial support for dash-renderer v23
May 4, 2019
0371634
resolved conflicts
May 4, 2019
73a429a
:bug: fix missing parenthesis
May 4, 2019
35dedac
rename pruned to pruned_errors
May 4, 2019
53b70dd
rename pruned to pruned_errors in utils.R also
May 4, 2019
151e838
:sparkles: add options for dev_tools_ui and dev_tools_props_check
May 4, 2019
140bf51
:sparkles: initial support for callback context
May 17, 2019
dd679f6
:hammer: deps handling changes to better support CSS
Jun 6, 2019
f521fd4
Improvements to debugging in Dash for R (#87)
rpkyle Jun 13, 2019
d9d66c1
:camel: fixes, :cow: fixes
Jun 13, 2019
f10e7ae
:camel: fixes
Jun 13, 2019
2fb324e
:necktie: remove stray parenthesis
Jun 13, 2019
dee5b71
Merge branch '0.0.7-dev' into 0.0.7-debug-CSS
rpkyle Jun 14, 2019
b4ab120
modify tag generation to allow modtimes
Jun 14, 2019
ba8ecf3
:hocho: callback_context initialization code
rpkyle Jun 17, 2019
006d3e7
Merge branch '0.1.0-dev' into 0.0.7-debug-CSS
rpkyle Jun 18, 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
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,8 @@ Imports:
assertthat,
digest,
base64enc,
mime
mime,
crayon
Suggests:
dashHtmlComponents (>= 0.16.0),
dashCoreComponents (>= 0.48.0),
Expand Down
208 changes: 148 additions & 60 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,16 +69,13 @@
#' provide [input] (and/or [state]) object(s) (which should reference
#' layout components) as argument value(s) to `func`.
#' }
#' \item{`run_server(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...)`}{
#' \item{`run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' Launch the application. If provided, `host`/`port` set
#' the `host`/`port` fields of the underlying [fiery::Fire] web
#' server. The `block`/`showcase`/`...` arguments are passed along
#' to the `ignite()` method of the [fiery::Fire] server.
#' }
#' \item{`run_heroku(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...)`}{
#' Like `run_server()` but sets sensible `host`/`port` defaults
#' for running the app on Heroku.
#' }
#' }
#'
#' @export
Expand DownExpand Up@@ -124,6 +121,7 @@ Dash <- R6::R6Class(
private$assets_url_path <- sub("/$", "", assets_url_path)
private$assets_ignore <- assets_ignore
private$suppress_callback_exceptions <- suppress_callback_exceptions

# config options
self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX")
self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX")
Expand DownExpand Up@@ -196,8 +194,8 @@ Dash <- R6::R6Class(

payload <- Map(function(callback_signature) {
list(
output=callback_signature$output,
inputs=callback_signature$inputs,
output=paste0(callback_signature$output, collapse="."),
state=callback_signature$state
)
}, private$callback_map)
Expand All@@ -220,8 +218,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]][['func']]
callback <- private$callback_map[[request$body$output]][['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 DownExpand Up@@ -257,24 +254,45 @@ Dash <- R6::R6Class(
}
}

output_value <- do.call(callback, callback_args)

# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), request$body$output$property)
# set the callback context associated with this invocation of the callback
private$callback_context_ <- setCallbackContext(request$body)

output_value <- getStackTrace(do.call(callback, callback_args),
debug = private$debug,
pruned_errors = private$pruned_errors)

# reset callback context
private$callback_context_ <- NULL

if (is.null(private$stack_message)) {
# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), gsub( "(^.+)(\\.)", "", request$body$output))
)
)
)

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'
} else if (private$debug==TRUE) {
# if there is an error, send it back to dash-renderer
response$body <- private$stack_message
response$status <- 500L
response$type <- 'html'
private$stack_message <- NULL
} else {
# if not in debug mode, do not return stack
response$body <- NULL
response$status <- 500L
private$stack_message <- NULL
}
TRUE
})

Expand All@@ -283,9 +301,9 @@ Dash <- R6::R6Class(
# https://github.com/plotly/dash/blob/1249ffbd051bfb5fdbe439612cbec7fa8fff5ab5/dash/dash.py#L488
# https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data
dash_suite <- paste0(self$config$routes_pathname_prefix, "_dash-component-suites/:package_name/:filename")
route$add_handler("get", dash_suite, function(request, response, keys, ...) {

route$add_handler("get", dash_suite, function(request, response, keys, ...) {
filename <- basename(file.path(keys$filename))

dep_list <- c(private$dependencies_internal,
private$dependencies,
private$dependencies_user)
Expand All@@ -295,18 +313,31 @@ Dash <- R6::R6Class(
clean_dependencies(dep_list)
)

dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
# return warning if a dependency goes unmatched, since the page
# will probably fail to render properly anyway without it
if (length(dep_pkg$rpkg_path) == 0) {
warning(sprintf("The dependency '%s' could not be loaded; the file was not found.",
filename),
call. = FALSE)

response$body <- NULL
response$status <- 404L
} else {
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
}

TRUE
})

Expand DownExpand Up@@ -443,27 +474,56 @@ Dash <- R6::R6Class(

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

# ------------------------------------------------------------------------
# request and return callback context
# ------------------------------------------------------------------------
callback_context = function() {
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context may only be accessed within a callback.")
}
private$callback_context_
},

# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...) {
if (!is.null(host)) self$server$host <- host
if (!is.null(port)) self$server$port <- as.numeric(port)
self$server$ignite(block = block, showcase = showcase, ...)
},
run_heroku = function(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...) {
run_server = function(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
port = Sys.getenv('DASH_PORT', 8050),
block = TRUE,
showcase = FALSE,
pruned_errors = TRUE,
debug = FALSE,
dev_tools_ui = NULL,
dev_tools_props_check = NULL,
...) {
self$server$host <- host
self$server$port <- as.numeric(port)
self$run_server(...)
}
),

if (debug & !(isFALSE(dev_tools_ui)) | isTRUE(dev_tools_ui)) {
self$config$ui <- TRUE
} else {
self$config$ui <- FALSE
}

if (debug & !(isFALSE(dev_tools_props_check)) | isTRUE(dev_tools_props_check)) {
self$config$props_check <- TRUE
} else {
self$config$props_check <- FALSE
}

private$pruned_errors <- pruned_errors
private$debug <- debug

self$server$ignite(block = block, showcase = showcase, ...)
}
),

private = list(
# private fields defined on initiation
Expand All@@ -479,7 +539,15 @@ Dash <- R6::R6Class(
css = NULL,
scripts = NULL,
other = NULL,


# initialize flags for debug mode and stack pruning,
debug = NULL,
pruned_errors = NULL,
stack_message = NULL,

# callback context
callback_context_ = NULL,

# fields for tracking HTML dependencies
dependencies = list(),
dependencies_user = list(),
Expand DownExpand Up@@ -566,9 +634,6 @@ Dash <- R6::R6Class(
# add on HTML dependencies we've identified by crawling the layout
private$dependencies <- c(private$dependencies, deps_layout)

# DashR's own dependencies
private$dependencies_internal <- dashR:::.dashR_js_metadata()

# return the computed layout
oldClass(layout_) <- c("dash_layout", oldClass(layout_))
layout_
Expand DownExpand Up@@ -676,7 +741,7 @@ Dash <- R6::R6Class(

# akin to https://github.com/plotly/dash-renderer/blob/master/dash_renderer/__init__.py
react_version_enabled= function() {
version <- private$dependencies_internal$react$version
version <- private$dependencies_internal$`react-prod`$version
return(version)
},
react_deps = function() {
Expand All@@ -692,16 +757,32 @@ Dash <- R6::R6Class(
.index = NULL,

collect_resources = function() {
# DashR's own dependencies
# serve the dev version of dash-renderer when in debug mode
dependencies_all_internal <- dashR:::.dashR_js_metadata()
if (private$debug) {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-prod",
"dash-renderer-map-prod")]
} else {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-dev",
"dash-renderer-map-dev")]
}

private$dependencies_internal <- depsSubset

# collect and resolve package dependencies
depsAll <- compact(c(
private$react_deps()[private$react_versions() %in% private$react_version_enabled()],
private$dependencies,
private$dependencies_user,
private$dependencies_internal[names(private$dependencies_internal) %in% 'dash-renderer']
private$dependencies_internal[grepl(pattern = "dash-renderer", x = private$dependencies_internal)]
))

# normalizes local paths and keeps newer versions of duplicates
depsAll <- htmltools::resolveDependencies(depsAll, FALSE)
depsAll <- depsAll[!vapply(depsAll,
function(v) {
!is.null(v[["script"]]) && tools::file_ext(v[["script"]]) == "map"
}, logical(1))]

# styleheets always go in header
css_deps <- compact(lapply(depsAll, function(dep) {
Expand DownExpand Up@@ -732,7 +813,8 @@ Dash <- R6::R6Class(
local = TRUE,
local_path = private$css,
prefix = self$config$requests_pathname_prefix)
} else {
}
else {
css_assets <- NULL
}

Expand DownExpand Up@@ -765,7 +847,13 @@ Dash <- R6::R6Class(
} else {
favicon <- ""
}


# set script tag to invoke a new dash_renderer
scripts_invoke_renderer <- sprintf("<script id=\"%s\" type=\"%s\">%s</script>",
"_dash-renderer",
"application/javascript",
"var renderer = new DashRenderer();")

# serving order of CSS and JS tags: package -> external -> assets
css_tags <- paste(c(css_deps,
css_external,
Expand All@@ -774,7 +862,8 @@ Dash <- R6::R6Class(

scripts_tags <- paste(c(scripts_deps,
scripts_external,
scripts_assets),
scripts_assets,
scripts_invoke_renderer),
collapse = "\n")

return(list(css_tags = css_tags,
Expand DownExpand Up@@ -822,7 +911,6 @@ Dash <- R6::R6Class(
to_JSON(self$config),
scripts_tags
)

}
)
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff9cc4d
Add support for 'subplots' from plot_ly and ggplotly in DashR (#84)
rpkyle Apr 18, 2019
fcb0972
Remove run_heroku() and inspect environment vars for setting host and…
rpkyle Apr 18, 2019
ca533f2
:bell: added warning when file goes unmatched (#81)
rpkyle Apr 22, 2019
689e309
:sparkles: initial support for stack tracing
Apr 23, 2019
cfe9c49
:truck: add dependency on crayon :package:
Apr 27, 2019
897df31
:scissors: trim call stack from top also
Apr 27, 2019
0ad3b38
renamed stack trace fn
Apr 27, 2019
0d526b4
:sparkles: capture errors as warnings in debug mode
Apr 27, 2019
6291553
Merge branch '0.0.7-dev' into 0.0.7-debug
rpkyle Apr 28, 2019
d84b964
:sparkles: initial support for dash-renderer v23
May 4, 2019
0371634
resolved conflicts
May 4, 2019
73a429a
:bug: fix missing parenthesis
May 4, 2019
35dedac
rename pruned to pruned_errors
May 4, 2019
53b70dd
rename pruned to pruned_errors in utils.R also
May 4, 2019
151e838
:sparkles: add options for dev_tools_ui and dev_tools_props_check
May 4, 2019
140bf51
:sparkles: initial support for callback context
May 17, 2019
dd679f6
:hammer: deps handling changes to better support CSS
Jun 6, 2019
f521fd4
Improvements to debugging in Dash for R (#87)
rpkyle Jun 13, 2019
d9d66c1
:camel: fixes, :cow: fixes
Jun 13, 2019
f10e7ae
:camel: fixes
Jun 13, 2019
2fb324e
:necktie: remove stray parenthesis
Jun 13, 2019
dee5b71
Merge branch '0.0.7-dev' into 0.0.7-debug-CSS
rpkyle Jun 14, 2019
b4ab120
modify tag generation to allow modtimes
Jun 14, 2019
ba8ecf3
:hocho: callback_context initialization code
rpkyle Jun 17, 2019
006d3e7
Merge branch '0.1.0-dev' into 0.0.7-debug-CSS
rpkyle Jun 18, 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
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,8 @@ Imports:
assertthat,
digest,
base64enc,
mime
mime,
crayon
Suggests:
dashHtmlComponents (>= 0.16.0),
dashCoreComponents (>= 0.48.0),
Expand Down
208 changes: 148 additions & 60 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,16 +69,13 @@
#' provide [input] (and/or [state]) object(s) (which should reference
#' layout components) as argument value(s) to `func`.
#' }
#' \item{`run_server(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...)`}{
#' \item{`run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' Launch the application. If provided, `host`/`port` set
#' the `host`/`port` fields of the underlying [fiery::Fire] web
#' server. The `block`/`showcase`/`...` arguments are passed along
#' to the `ignite()` method of the [fiery::Fire] server.
#' }
#' \item{`run_heroku(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...)`}{
#' Like `run_server()` but sets sensible `host`/`port` defaults
#' for running the app on Heroku.
#' }
#' }
#'
#' @export
Expand DownExpand Up@@ -124,6 +121,7 @@ Dash <- R6::R6Class(
private$assets_url_path <- sub("/$", "", assets_url_path)
private$assets_ignore <- assets_ignore
private$suppress_callback_exceptions <- suppress_callback_exceptions

# config options
self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX")
self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX")
Expand DownExpand Up@@ -196,8 +194,8 @@ Dash <- R6::R6Class(

payload <- Map(function(callback_signature) {
list(
output=callback_signature$output,
inputs=callback_signature$inputs,
output=paste0(callback_signature$output, collapse="."),
state=callback_signature$state
)
}, private$callback_map)
Expand All@@ -220,8 +218,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]][['func']]
callback <- private$callback_map[[request$body$output]][['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 DownExpand Up@@ -257,24 +254,45 @@ Dash <- R6::R6Class(
}
}

output_value <- do.call(callback, callback_args)

# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), request$body$output$property)
# set the callback context associated with this invocation of the callback
private$callback_context_ <- setCallbackContext(request$body)

output_value <- getStackTrace(do.call(callback, callback_args),
debug = private$debug,
pruned_errors = private$pruned_errors)

# reset callback context
private$callback_context_ <- NULL

if (is.null(private$stack_message)) {
# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), gsub( "(^.+)(\\.)", "", request$body$output))
)
)
)

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'
} else if (private$debug==TRUE) {
# if there is an error, send it back to dash-renderer
response$body <- private$stack_message
response$status <- 500L
response$type <- 'html'
private$stack_message <- NULL
} else {
# if not in debug mode, do not return stack
response$body <- NULL
response$status <- 500L
private$stack_message <- NULL
}
TRUE
})

Expand All@@ -283,9 +301,9 @@ Dash <- R6::R6Class(
# https://github.com/plotly/dash/blob/1249ffbd051bfb5fdbe439612cbec7fa8fff5ab5/dash/dash.py#L488
# https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data
dash_suite <- paste0(self$config$routes_pathname_prefix, "_dash-component-suites/:package_name/:filename")
route$add_handler("get", dash_suite, function(request, response, keys, ...) {

route$add_handler("get", dash_suite, function(request, response, keys, ...) {
filename <- basename(file.path(keys$filename))

dep_list <- c(private$dependencies_internal,
private$dependencies,
private$dependencies_user)
Expand All@@ -295,18 +313,31 @@ Dash <- R6::R6Class(
clean_dependencies(dep_list)
)

dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
# return warning if a dependency goes unmatched, since the page
# will probably fail to render properly anyway without it
if (length(dep_pkg$rpkg_path) == 0) {
warning(sprintf("The dependency '%s' could not be loaded; the file was not found.",
filename),
call. = FALSE)

response$body <- NULL
response$status <- 404L
} else {
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
}

TRUE
})

Expand DownExpand Up@@ -443,27 +474,56 @@ Dash <- R6::R6Class(

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

# ------------------------------------------------------------------------
# request and return callback context
# ------------------------------------------------------------------------
callback_context = function() {
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context may only be accessed within a callback.")
}
private$callback_context_
},

# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...) {
if (!is.null(host)) self$server$host <- host
if (!is.null(port)) self$server$port <- as.numeric(port)
self$server$ignite(block = block, showcase = showcase, ...)
},
run_heroku = function(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...) {
run_server = function(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
port = Sys.getenv('DASH_PORT', 8050),
block = TRUE,
showcase = FALSE,
pruned_errors = TRUE,
debug = FALSE,
dev_tools_ui = NULL,
dev_tools_props_check = NULL,
...) {
self$server$host <- host
self$server$port <- as.numeric(port)
self$run_server(...)
}
),

if (debug & !(isFALSE(dev_tools_ui)) | isTRUE(dev_tools_ui)) {
self$config$ui <- TRUE
} else {
self$config$ui <- FALSE
}

if (debug & !(isFALSE(dev_tools_props_check)) | isTRUE(dev_tools_props_check)) {
self$config$props_check <- TRUE
} else {
self$config$props_check <- FALSE
}

private$pruned_errors <- pruned_errors
private$debug <- debug

self$server$ignite(block = block, showcase = showcase, ...)
}
),

private = list(
# private fields defined on initiation
Expand All@@ -479,7 +539,15 @@ Dash <- R6::R6Class(
css = NULL,
scripts = NULL,
other = NULL,


# initialize flags for debug mode and stack pruning,
debug = NULL,
pruned_errors = NULL,
stack_message = NULL,

# callback context
callback_context_ = NULL,

# fields for tracking HTML dependencies
dependencies = list(),
dependencies_user = list(),
Expand DownExpand Up@@ -566,9 +634,6 @@ Dash <- R6::R6Class(
# add on HTML dependencies we've identified by crawling the layout
private$dependencies <- c(private$dependencies, deps_layout)

# DashR's own dependencies
private$dependencies_internal <- dashR:::.dashR_js_metadata()

# return the computed layout
oldClass(layout_) <- c("dash_layout", oldClass(layout_))
layout_
Expand DownExpand Up@@ -676,7 +741,7 @@ Dash <- R6::R6Class(

# akin to https://github.com/plotly/dash-renderer/blob/master/dash_renderer/__init__.py
react_version_enabled= function() {
version <- private$dependencies_internal$react$version
version <- private$dependencies_internal$`react-prod`$version
return(version)
},
react_deps = function() {
Expand All@@ -692,16 +757,32 @@ Dash <- R6::R6Class(
.index = NULL,

collect_resources = function() {
# DashR's own dependencies
# serve the dev version of dash-renderer when in debug mode
dependencies_all_internal <- dashR:::.dashR_js_metadata()
if (private$debug) {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-prod",
"dash-renderer-map-prod")]
} else {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-dev",
"dash-renderer-map-dev")]
}

private$dependencies_internal <- depsSubset

# collect and resolve package dependencies
depsAll <- compact(c(
private$react_deps()[private$react_versions() %in% private$react_version_enabled()],
private$dependencies,
private$dependencies_user,
private$dependencies_internal[names(private$dependencies_internal) %in% 'dash-renderer']
private$dependencies_internal[grepl(pattern = "dash-renderer", x = private$dependencies_internal)]
))

# normalizes local paths and keeps newer versions of duplicates
depsAll <- htmltools::resolveDependencies(depsAll, FALSE)
depsAll <- depsAll[!vapply(depsAll,
function(v) {
!is.null(v[["script"]]) && tools::file_ext(v[["script"]]) == "map"
}, logical(1))]

# styleheets always go in header
css_deps <- compact(lapply(depsAll, function(dep) {
Expand DownExpand Up@@ -732,7 +813,8 @@ Dash <- R6::R6Class(
local = TRUE,
local_path = private$css,
prefix = self$config$requests_pathname_prefix)
} else {
}
else {
css_assets <- NULL
}

Expand DownExpand Up@@ -765,7 +847,13 @@ Dash <- R6::R6Class(
} else {
favicon <- ""
}


# set script tag to invoke a new dash_renderer
scripts_invoke_renderer <- sprintf("<script id=\"%s\" type=\"%s\">%s</script>",
"_dash-renderer",
"application/javascript",
"var renderer = new DashRenderer();")

# serving order of CSS and JS tags: package -> external -> assets
css_tags <- paste(c(css_deps,
css_external,
Expand All@@ -774,7 +862,8 @@ Dash <- R6::R6Class(

scripts_tags <- paste(c(scripts_deps,
scripts_external,
scripts_assets),
scripts_assets,
scripts_invoke_renderer),
collapse = "\n")

return(list(css_tags = css_tags,
Expand DownExpand Up@@ -822,7 +911,6 @@ Dash <- R6::R6Class(
to_JSON(self$config),
scripts_tags
)

}
)
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff9cc4d
Add support for 'subplots' from plot_ly and ggplotly in DashR (#84)
rpkyle Apr 18, 2019
fcb0972
Remove run_heroku() and inspect environment vars for setting host and…
rpkyle Apr 18, 2019
ca533f2
:bell: added warning when file goes unmatched (#81)
rpkyle Apr 22, 2019
689e309
:sparkles: initial support for stack tracing
Apr 23, 2019
cfe9c49
:truck: add dependency on crayon :package:
Apr 27, 2019
897df31
:scissors: trim call stack from top also
Apr 27, 2019
0ad3b38
renamed stack trace fn
Apr 27, 2019
0d526b4
:sparkles: capture errors as warnings in debug mode
Apr 27, 2019
6291553
Merge branch '0.0.7-dev' into 0.0.7-debug
rpkyle Apr 28, 2019
d84b964
:sparkles: initial support for dash-renderer v23
May 4, 2019
0371634
resolved conflicts
May 4, 2019
73a429a
:bug: fix missing parenthesis
May 4, 2019
35dedac
rename pruned to pruned_errors
May 4, 2019
53b70dd
rename pruned to pruned_errors in utils.R also
May 4, 2019
151e838
:sparkles: add options for dev_tools_ui and dev_tools_props_check
May 4, 2019
140bf51
:sparkles: initial support for callback context
May 17, 2019
dd679f6
:hammer: deps handling changes to better support CSS
Jun 6, 2019
f521fd4
Improvements to debugging in Dash for R (#87)
rpkyle Jun 13, 2019
d9d66c1
:camel: fixes, :cow: fixes
Jun 13, 2019
f10e7ae
:camel: fixes
Jun 13, 2019
2fb324e
:necktie: remove stray parenthesis
Jun 13, 2019
dee5b71
Merge branch '0.0.7-dev' into 0.0.7-debug-CSS
rpkyle Jun 14, 2019
b4ab120
modify tag generation to allow modtimes
Jun 14, 2019
ba8ecf3
:hocho: callback_context initialization code
rpkyle Jun 17, 2019
006d3e7
Merge branch '0.1.0-dev' into 0.0.7-debug-CSS
rpkyle Jun 18, 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
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,8 @@ Imports:
assertthat,
digest,
base64enc,
mime
mime,
crayon
Suggests:
dashHtmlComponents (>= 0.16.0),
dashCoreComponents (>= 0.48.0),
Expand Down
208 changes: 148 additions & 60 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,16 +69,13 @@
#' provide [input] (and/or [state]) object(s) (which should reference
#' layout components) as argument value(s) to `func`.
#' }
#' \item{`run_server(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...)`}{
#' \item{`run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' Launch the application. If provided, `host`/`port` set
#' the `host`/`port` fields of the underlying [fiery::Fire] web
#' server. The `block`/`showcase`/`...` arguments are passed along
#' to the `ignite()` method of the [fiery::Fire] server.
#' }
#' \item{`run_heroku(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...)`}{
#' Like `run_server()` but sets sensible `host`/`port` defaults
#' for running the app on Heroku.
#' }
#' }
#'
#' @export
Expand DownExpand Up@@ -124,6 +121,7 @@ Dash <- R6::R6Class(
private$assets_url_path <- sub("/$", "", assets_url_path)
private$assets_ignore <- assets_ignore
private$suppress_callback_exceptions <- suppress_callback_exceptions

# config options
self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX")
self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX")
Expand DownExpand Up@@ -196,8 +194,8 @@ Dash <- R6::R6Class(

payload <- Map(function(callback_signature) {
list(
output=callback_signature$output,
inputs=callback_signature$inputs,
output=paste0(callback_signature$output, collapse="."),
state=callback_signature$state
)
}, private$callback_map)
Expand All@@ -220,8 +218,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]][['func']]
callback <- private$callback_map[[request$body$output]][['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 DownExpand Up@@ -257,24 +254,45 @@ Dash <- R6::R6Class(
}
}

output_value <- do.call(callback, callback_args)

# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), request$body$output$property)
# set the callback context associated with this invocation of the callback
private$callback_context_ <- setCallbackContext(request$body)

output_value <- getStackTrace(do.call(callback, callback_args),
debug = private$debug,
pruned_errors = private$pruned_errors)

# reset callback context
private$callback_context_ <- NULL

if (is.null(private$stack_message)) {
# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), gsub( "(^.+)(\\.)", "", request$body$output))
)
)
)

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'
} else if (private$debug==TRUE) {
# if there is an error, send it back to dash-renderer
response$body <- private$stack_message
response$status <- 500L
response$type <- 'html'
private$stack_message <- NULL
} else {
# if not in debug mode, do not return stack
response$body <- NULL
response$status <- 500L
private$stack_message <- NULL
}
TRUE
})

Expand All@@ -283,9 +301,9 @@ Dash <- R6::R6Class(
# https://github.com/plotly/dash/blob/1249ffbd051bfb5fdbe439612cbec7fa8fff5ab5/dash/dash.py#L488
# https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data
dash_suite <- paste0(self$config$routes_pathname_prefix, "_dash-component-suites/:package_name/:filename")
route$add_handler("get", dash_suite, function(request, response, keys, ...) {

route$add_handler("get", dash_suite, function(request, response, keys, ...) {
filename <- basename(file.path(keys$filename))

dep_list <- c(private$dependencies_internal,
private$dependencies,
private$dependencies_user)
Expand All@@ -295,18 +313,31 @@ Dash <- R6::R6Class(
clean_dependencies(dep_list)
)

dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
# return warning if a dependency goes unmatched, since the page
# will probably fail to render properly anyway without it
if (length(dep_pkg$rpkg_path) == 0) {
warning(sprintf("The dependency '%s' could not be loaded; the file was not found.",
filename),
call. = FALSE)

response$body <- NULL
response$status <- 404L
} else {
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
}

TRUE
})

Expand DownExpand Up@@ -443,27 +474,56 @@ Dash <- R6::R6Class(

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

# ------------------------------------------------------------------------
# request and return callback context
# ------------------------------------------------------------------------
callback_context = function() {
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context may only be accessed within a callback.")
}
private$callback_context_
},

# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...) {
if (!is.null(host)) self$server$host <- host
if (!is.null(port)) self$server$port <- as.numeric(port)
self$server$ignite(block = block, showcase = showcase, ...)
},
run_heroku = function(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...) {
run_server = function(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
port = Sys.getenv('DASH_PORT', 8050),
block = TRUE,
showcase = FALSE,
pruned_errors = TRUE,
debug = FALSE,
dev_tools_ui = NULL,
dev_tools_props_check = NULL,
...) {
self$server$host <- host
self$server$port <- as.numeric(port)
self$run_server(...)
}
),

if (debug & !(isFALSE(dev_tools_ui)) | isTRUE(dev_tools_ui)) {
self$config$ui <- TRUE
} else {
self$config$ui <- FALSE
}

if (debug & !(isFALSE(dev_tools_props_check)) | isTRUE(dev_tools_props_check)) {
self$config$props_check <- TRUE
} else {
self$config$props_check <- FALSE
}

private$pruned_errors <- pruned_errors
private$debug <- debug

self$server$ignite(block = block, showcase = showcase, ...)
}
),

private = list(
# private fields defined on initiation
Expand All@@ -479,7 +539,15 @@ Dash <- R6::R6Class(
css = NULL,
scripts = NULL,
other = NULL,


# initialize flags for debug mode and stack pruning,
debug = NULL,
pruned_errors = NULL,
stack_message = NULL,

# callback context
callback_context_ = NULL,

# fields for tracking HTML dependencies
dependencies = list(),
dependencies_user = list(),
Expand DownExpand Up@@ -566,9 +634,6 @@ Dash <- R6::R6Class(
# add on HTML dependencies we've identified by crawling the layout
private$dependencies <- c(private$dependencies, deps_layout)

# DashR's own dependencies
private$dependencies_internal <- dashR:::.dashR_js_metadata()

# return the computed layout
oldClass(layout_) <- c("dash_layout", oldClass(layout_))
layout_
Expand DownExpand Up@@ -676,7 +741,7 @@ Dash <- R6::R6Class(

# akin to https://github.com/plotly/dash-renderer/blob/master/dash_renderer/__init__.py
react_version_enabled= function() {
version <- private$dependencies_internal$react$version
version <- private$dependencies_internal$`react-prod`$version
return(version)
},
react_deps = function() {
Expand All@@ -692,16 +757,32 @@ Dash <- R6::R6Class(
.index = NULL,

collect_resources = function() {
# DashR's own dependencies
# serve the dev version of dash-renderer when in debug mode
dependencies_all_internal <- dashR:::.dashR_js_metadata()
if (private$debug) {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-prod",
"dash-renderer-map-prod")]
} else {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-dev",
"dash-renderer-map-dev")]
}

private$dependencies_internal <- depsSubset

# collect and resolve package dependencies
depsAll <- compact(c(
private$react_deps()[private$react_versions() %in% private$react_version_enabled()],
private$dependencies,
private$dependencies_user,
private$dependencies_internal[names(private$dependencies_internal) %in% 'dash-renderer']
private$dependencies_internal[grepl(pattern = "dash-renderer", x = private$dependencies_internal)]
))

# normalizes local paths and keeps newer versions of duplicates
depsAll <- htmltools::resolveDependencies(depsAll, FALSE)
depsAll <- depsAll[!vapply(depsAll,
function(v) {
!is.null(v[["script"]]) && tools::file_ext(v[["script"]]) == "map"
}, logical(1))]

# styleheets always go in header
css_deps <- compact(lapply(depsAll, function(dep) {
Expand DownExpand Up@@ -732,7 +813,8 @@ Dash <- R6::R6Class(
local = TRUE,
local_path = private$css,
prefix = self$config$requests_pathname_prefix)
} else {
}
else {
css_assets <- NULL
}

Expand DownExpand Up@@ -765,7 +847,13 @@ Dash <- R6::R6Class(
} else {
favicon <- ""
}


# set script tag to invoke a new dash_renderer
scripts_invoke_renderer <- sprintf("<script id=\"%s\" type=\"%s\">%s</script>",
"_dash-renderer",
"application/javascript",
"var renderer = new DashRenderer();")

# serving order of CSS and JS tags: package -> external -> assets
css_tags <- paste(c(css_deps,
css_external,
Expand All@@ -774,7 +862,8 @@ Dash <- R6::R6Class(

scripts_tags <- paste(c(scripts_deps,
scripts_external,
scripts_assets),
scripts_assets,
scripts_invoke_renderer),
collapse = "\n")

return(list(css_tags = css_tags,
Expand DownExpand Up@@ -822,7 +911,6 @@ Dash <- R6::R6Class(
to_JSON(self$config),
scripts_tags
)

}
)
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff9cc4d
Add support for 'subplots' from plot_ly and ggplotly in DashR (#84)
rpkyle Apr 18, 2019
fcb0972
Remove run_heroku() and inspect environment vars for setting host and…
rpkyle Apr 18, 2019
ca533f2
:bell: added warning when file goes unmatched (#81)
rpkyle Apr 22, 2019
689e309
:sparkles: initial support for stack tracing
Apr 23, 2019
cfe9c49
:truck: add dependency on crayon :package:
Apr 27, 2019
897df31
:scissors: trim call stack from top also
Apr 27, 2019
0ad3b38
renamed stack trace fn
Apr 27, 2019
0d526b4
:sparkles: capture errors as warnings in debug mode
Apr 27, 2019
6291553
Merge branch '0.0.7-dev' into 0.0.7-debug
rpkyle Apr 28, 2019
d84b964
:sparkles: initial support for dash-renderer v23
May 4, 2019
0371634
resolved conflicts
May 4, 2019
73a429a
:bug: fix missing parenthesis
May 4, 2019
35dedac
rename pruned to pruned_errors
May 4, 2019
53b70dd
rename pruned to pruned_errors in utils.R also
May 4, 2019
151e838
:sparkles: add options for dev_tools_ui and dev_tools_props_check
May 4, 2019
140bf51
:sparkles: initial support for callback context
May 17, 2019
dd679f6
:hammer: deps handling changes to better support CSS
Jun 6, 2019
f521fd4
Improvements to debugging in Dash for R (#87)
rpkyle Jun 13, 2019
d9d66c1
:camel: fixes, :cow: fixes
Jun 13, 2019
f10e7ae
:camel: fixes
Jun 13, 2019
2fb324e
:necktie: remove stray parenthesis
Jun 13, 2019
dee5b71
Merge branch '0.0.7-dev' into 0.0.7-debug-CSS
rpkyle Jun 14, 2019
b4ab120
modify tag generation to allow modtimes
Jun 14, 2019
ba8ecf3
:hocho: callback_context initialization code
rpkyle Jun 17, 2019
006d3e7
Merge branch '0.1.0-dev' into 0.0.7-debug-CSS
rpkyle Jun 18, 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
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,8 @@ Imports:
assertthat,
digest,
base64enc,
mime
mime,
crayon
Suggests:
dashHtmlComponents (>= 0.16.0),
dashCoreComponents (>= 0.48.0),
Expand Down
208 changes: 148 additions & 60 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,16 +69,13 @@
#' provide [input] (and/or [state]) object(s) (which should reference
#' layout components) as argument value(s) to `func`.
#' }
#' \item{`run_server(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...)`}{
#' \item{`run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' Launch the application. If provided, `host`/`port` set
#' the `host`/`port` fields of the underlying [fiery::Fire] web
#' server. The `block`/`showcase`/`...` arguments are passed along
#' to the `ignite()` method of the [fiery::Fire] server.
#' }
#' \item{`run_heroku(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...)`}{
#' Like `run_server()` but sets sensible `host`/`port` defaults
#' for running the app on Heroku.
#' }
#' }
#'
#' @export
Expand DownExpand Up@@ -124,6 +121,7 @@ Dash <- R6::R6Class(
private$assets_url_path <- sub("/$", "", assets_url_path)
private$assets_ignore <- assets_ignore
private$suppress_callback_exceptions <- suppress_callback_exceptions

# config options
self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX")
self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX")
Expand DownExpand Up@@ -196,8 +194,8 @@ Dash <- R6::R6Class(

payload <- Map(function(callback_signature) {
list(
output=callback_signature$output,
inputs=callback_signature$inputs,
output=paste0(callback_signature$output, collapse="."),
state=callback_signature$state
)
}, private$callback_map)
Expand All@@ -220,8 +218,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]][['func']]
callback <- private$callback_map[[request$body$output]][['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 DownExpand Up@@ -257,24 +254,45 @@ Dash <- R6::R6Class(
}
}

output_value <- do.call(callback, callback_args)

# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), request$body$output$property)
# set the callback context associated with this invocation of the callback
private$callback_context_ <- setCallbackContext(request$body)

output_value <- getStackTrace(do.call(callback, callback_args),
debug = private$debug,
pruned_errors = private$pruned_errors)

# reset callback context
private$callback_context_ <- NULL

if (is.null(private$stack_message)) {
# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), gsub( "(^.+)(\\.)", "", request$body$output))
)
)
)

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'
} else if (private$debug==TRUE) {
# if there is an error, send it back to dash-renderer
response$body <- private$stack_message
response$status <- 500L
response$type <- 'html'
private$stack_message <- NULL
} else {
# if not in debug mode, do not return stack
response$body <- NULL
response$status <- 500L
private$stack_message <- NULL
}
TRUE
})

Expand All@@ -283,9 +301,9 @@ Dash <- R6::R6Class(
# https://github.com/plotly/dash/blob/1249ffbd051bfb5fdbe439612cbec7fa8fff5ab5/dash/dash.py#L488
# https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data
dash_suite <- paste0(self$config$routes_pathname_prefix, "_dash-component-suites/:package_name/:filename")
route$add_handler("get", dash_suite, function(request, response, keys, ...) {

route$add_handler("get", dash_suite, function(request, response, keys, ...) {
filename <- basename(file.path(keys$filename))

dep_list <- c(private$dependencies_internal,
private$dependencies,
private$dependencies_user)
Expand All@@ -295,18 +313,31 @@ Dash <- R6::R6Class(
clean_dependencies(dep_list)
)

dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
# return warning if a dependency goes unmatched, since the page
# will probably fail to render properly anyway without it
if (length(dep_pkg$rpkg_path) == 0) {
warning(sprintf("The dependency '%s' could not be loaded; the file was not found.",
filename),
call. = FALSE)

response$body <- NULL
response$status <- 404L
} else {
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
}

TRUE
})

Expand DownExpand Up@@ -443,27 +474,56 @@ Dash <- R6::R6Class(

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

# ------------------------------------------------------------------------
# request and return callback context
# ------------------------------------------------------------------------
callback_context = function() {
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context may only be accessed within a callback.")
}
private$callback_context_
},

# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...) {
if (!is.null(host)) self$server$host <- host
if (!is.null(port)) self$server$port <- as.numeric(port)
self$server$ignite(block = block, showcase = showcase, ...)
},
run_heroku = function(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...) {
run_server = function(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
port = Sys.getenv('DASH_PORT', 8050),
block = TRUE,
showcase = FALSE,
pruned_errors = TRUE,
debug = FALSE,
dev_tools_ui = NULL,
dev_tools_props_check = NULL,
...) {
self$server$host <- host
self$server$port <- as.numeric(port)
self$run_server(...)
}
),

if (debug & !(isFALSE(dev_tools_ui)) | isTRUE(dev_tools_ui)) {
self$config$ui <- TRUE
} else {
self$config$ui <- FALSE
}

if (debug & !(isFALSE(dev_tools_props_check)) | isTRUE(dev_tools_props_check)) {
self$config$props_check <- TRUE
} else {
self$config$props_check <- FALSE
}

private$pruned_errors <- pruned_errors
private$debug <- debug

self$server$ignite(block = block, showcase = showcase, ...)
}
),

private = list(
# private fields defined on initiation
Expand All@@ -479,7 +539,15 @@ Dash <- R6::R6Class(
css = NULL,
scripts = NULL,
other = NULL,


# initialize flags for debug mode and stack pruning,
debug = NULL,
pruned_errors = NULL,
stack_message = NULL,

# callback context
callback_context_ = NULL,

# fields for tracking HTML dependencies
dependencies = list(),
dependencies_user = list(),
Expand DownExpand Up@@ -566,9 +634,6 @@ Dash <- R6::R6Class(
# add on HTML dependencies we've identified by crawling the layout
private$dependencies <- c(private$dependencies, deps_layout)

# DashR's own dependencies
private$dependencies_internal <- dashR:::.dashR_js_metadata()

# return the computed layout
oldClass(layout_) <- c("dash_layout", oldClass(layout_))
layout_
Expand DownExpand Up@@ -676,7 +741,7 @@ Dash <- R6::R6Class(

# akin to https://github.com/plotly/dash-renderer/blob/master/dash_renderer/__init__.py
react_version_enabled= function() {
version <- private$dependencies_internal$react$version
version <- private$dependencies_internal$`react-prod`$version
return(version)
},
react_deps = function() {
Expand All@@ -692,16 +757,32 @@ Dash <- R6::R6Class(
.index = NULL,

collect_resources = function() {
# DashR's own dependencies
# serve the dev version of dash-renderer when in debug mode
dependencies_all_internal <- dashR:::.dashR_js_metadata()
if (private$debug) {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-prod",
"dash-renderer-map-prod")]
} else {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-dev",
"dash-renderer-map-dev")]
}

private$dependencies_internal <- depsSubset

# collect and resolve package dependencies
depsAll <- compact(c(
private$react_deps()[private$react_versions() %in% private$react_version_enabled()],
private$dependencies,
private$dependencies_user,
private$dependencies_internal[names(private$dependencies_internal) %in% 'dash-renderer']
private$dependencies_internal[grepl(pattern = "dash-renderer", x = private$dependencies_internal)]
))

# normalizes local paths and keeps newer versions of duplicates
depsAll <- htmltools::resolveDependencies(depsAll, FALSE)
depsAll <- depsAll[!vapply(depsAll,
function(v) {
!is.null(v[["script"]]) && tools::file_ext(v[["script"]]) == "map"
}, logical(1))]

# styleheets always go in header
css_deps <- compact(lapply(depsAll, function(dep) {
Expand DownExpand Up@@ -732,7 +813,8 @@ Dash <- R6::R6Class(
local = TRUE,
local_path = private$css,
prefix = self$config$requests_pathname_prefix)
} else {
}
else {
css_assets <- NULL
}

Expand DownExpand Up@@ -765,7 +847,13 @@ Dash <- R6::R6Class(
} else {
favicon <- ""
}


# set script tag to invoke a new dash_renderer
scripts_invoke_renderer <- sprintf("<script id=\"%s\" type=\"%s\">%s</script>",
"_dash-renderer",
"application/javascript",
"var renderer = new DashRenderer();")

# serving order of CSS and JS tags: package -> external -> assets
css_tags <- paste(c(css_deps,
css_external,
Expand All@@ -774,7 +862,8 @@ Dash <- R6::R6Class(

scripts_tags <- paste(c(scripts_deps,
scripts_external,
scripts_assets),
scripts_assets,
scripts_invoke_renderer),
collapse = "\n")

return(list(css_tags = css_tags,
Expand DownExpand Up@@ -822,7 +911,6 @@ Dash <- R6::R6Class(
to_JSON(self$config),
scripts_tags
)

}
)
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff9cc4d
Add support for 'subplots' from plot_ly and ggplotly in DashR (#84)
rpkyle Apr 18, 2019
fcb0972
Remove run_heroku() and inspect environment vars for setting host and…
rpkyle Apr 18, 2019
ca533f2
:bell: added warning when file goes unmatched (#81)
rpkyle Apr 22, 2019
689e309
:sparkles: initial support for stack tracing
Apr 23, 2019
cfe9c49
:truck: add dependency on crayon :package:
Apr 27, 2019
897df31
:scissors: trim call stack from top also
Apr 27, 2019
0ad3b38
renamed stack trace fn
Apr 27, 2019
0d526b4
:sparkles: capture errors as warnings in debug mode
Apr 27, 2019
6291553
Merge branch '0.0.7-dev' into 0.0.7-debug
rpkyle Apr 28, 2019
d84b964
:sparkles: initial support for dash-renderer v23
May 4, 2019
0371634
resolved conflicts
May 4, 2019
73a429a
:bug: fix missing parenthesis
May 4, 2019
35dedac
rename pruned to pruned_errors
May 4, 2019
53b70dd
rename pruned to pruned_errors in utils.R also
May 4, 2019
151e838
:sparkles: add options for dev_tools_ui and dev_tools_props_check
May 4, 2019
140bf51
:sparkles: initial support for callback context
May 17, 2019
dd679f6
:hammer: deps handling changes to better support CSS
Jun 6, 2019
f521fd4
Improvements to debugging in Dash for R (#87)
rpkyle Jun 13, 2019
d9d66c1
:camel: fixes, :cow: fixes
Jun 13, 2019
f10e7ae
:camel: fixes
Jun 13, 2019
2fb324e
:necktie: remove stray parenthesis
Jun 13, 2019
dee5b71
Merge branch '0.0.7-dev' into 0.0.7-debug-CSS
rpkyle Jun 14, 2019
b4ab120
modify tag generation to allow modtimes
Jun 14, 2019
ba8ecf3
:hocho: callback_context initialization code
rpkyle Jun 17, 2019
006d3e7
Merge branch '0.1.0-dev' into 0.0.7-debug-CSS
rpkyle Jun 18, 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
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,8 @@ Imports:
assertthat,
digest,
base64enc,
mime
mime,
crayon
Suggests:
dashHtmlComponents (>= 0.16.0),
dashCoreComponents (>= 0.48.0),
Expand Down
208 changes: 148 additions & 60 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,16 +69,13 @@
#' provide [input] (and/or [state]) object(s) (which should reference
#' layout components) as argument value(s) to `func`.
#' }
#' \item{`run_server(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...)`}{
#' \item{`run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' Launch the application. If provided, `host`/`port` set
#' the `host`/`port` fields of the underlying [fiery::Fire] web
#' server. The `block`/`showcase`/`...` arguments are passed along
#' to the `ignite()` method of the [fiery::Fire] server.
#' }
#' \item{`run_heroku(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...)`}{
#' Like `run_server()` but sets sensible `host`/`port` defaults
#' for running the app on Heroku.
#' }
#' }
#'
#' @export
Expand DownExpand Up@@ -124,6 +121,7 @@ Dash <- R6::R6Class(
private$assets_url_path <- sub("/$", "", assets_url_path)
private$assets_ignore <- assets_ignore
private$suppress_callback_exceptions <- suppress_callback_exceptions

# config options
self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX")
self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX")
Expand DownExpand Up@@ -196,8 +194,8 @@ Dash <- R6::R6Class(

payload <- Map(function(callback_signature) {
list(
output=callback_signature$output,
inputs=callback_signature$inputs,
output=paste0(callback_signature$output, collapse="."),
state=callback_signature$state
)
}, private$callback_map)
Expand All@@ -220,8 +218,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]][['func']]
callback <- private$callback_map[[request$body$output]][['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 DownExpand Up@@ -257,24 +254,45 @@ Dash <- R6::R6Class(
}
}

output_value <- do.call(callback, callback_args)

# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), request$body$output$property)
# set the callback context associated with this invocation of the callback
private$callback_context_ <- setCallbackContext(request$body)

output_value <- getStackTrace(do.call(callback, callback_args),
debug = private$debug,
pruned_errors = private$pruned_errors)

# reset callback context
private$callback_context_ <- NULL

if (is.null(private$stack_message)) {
# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), gsub( "(^.+)(\\.)", "", request$body$output))
)
)
)

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'
} else if (private$debug==TRUE) {
# if there is an error, send it back to dash-renderer
response$body <- private$stack_message
response$status <- 500L
response$type <- 'html'
private$stack_message <- NULL
} else {
# if not in debug mode, do not return stack
response$body <- NULL
response$status <- 500L
private$stack_message <- NULL
}
TRUE
})

Expand All@@ -283,9 +301,9 @@ Dash <- R6::R6Class(
# https://github.com/plotly/dash/blob/1249ffbd051bfb5fdbe439612cbec7fa8fff5ab5/dash/dash.py#L488
# https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data
dash_suite <- paste0(self$config$routes_pathname_prefix, "_dash-component-suites/:package_name/:filename")
route$add_handler("get", dash_suite, function(request, response, keys, ...) {

route$add_handler("get", dash_suite, function(request, response, keys, ...) {
filename <- basename(file.path(keys$filename))

dep_list <- c(private$dependencies_internal,
private$dependencies,
private$dependencies_user)
Expand All@@ -295,18 +313,31 @@ Dash <- R6::R6Class(
clean_dependencies(dep_list)
)

dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
# return warning if a dependency goes unmatched, since the page
# will probably fail to render properly anyway without it
if (length(dep_pkg$rpkg_path) == 0) {
warning(sprintf("The dependency '%s' could not be loaded; the file was not found.",
filename),
call. = FALSE)

response$body <- NULL
response$status <- 404L
} else {
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
}

TRUE
})

Expand DownExpand Up@@ -443,27 +474,56 @@ Dash <- R6::R6Class(

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

# ------------------------------------------------------------------------
# request and return callback context
# ------------------------------------------------------------------------
callback_context = function() {
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context may only be accessed within a callback.")
}
private$callback_context_
},

# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...) {
if (!is.null(host)) self$server$host <- host
if (!is.null(port)) self$server$port <- as.numeric(port)
self$server$ignite(block = block, showcase = showcase, ...)
},
run_heroku = function(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...) {
run_server = function(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
port = Sys.getenv('DASH_PORT', 8050),
block = TRUE,
showcase = FALSE,
pruned_errors = TRUE,
debug = FALSE,
dev_tools_ui = NULL,
dev_tools_props_check = NULL,
...) {
self$server$host <- host
self$server$port <- as.numeric(port)
self$run_server(...)
}
),

if (debug & !(isFALSE(dev_tools_ui)) | isTRUE(dev_tools_ui)) {
self$config$ui <- TRUE
} else {
self$config$ui <- FALSE
}

if (debug & !(isFALSE(dev_tools_props_check)) | isTRUE(dev_tools_props_check)) {
self$config$props_check <- TRUE
} else {
self$config$props_check <- FALSE
}

private$pruned_errors <- pruned_errors
private$debug <- debug

self$server$ignite(block = block, showcase = showcase, ...)
}
),

private = list(
# private fields defined on initiation
Expand All@@ -479,7 +539,15 @@ Dash <- R6::R6Class(
css = NULL,
scripts = NULL,
other = NULL,


# initialize flags for debug mode and stack pruning,
debug = NULL,
pruned_errors = NULL,
stack_message = NULL,

# callback context
callback_context_ = NULL,

# fields for tracking HTML dependencies
dependencies = list(),
dependencies_user = list(),
Expand DownExpand Up@@ -566,9 +634,6 @@ Dash <- R6::R6Class(
# add on HTML dependencies we've identified by crawling the layout
private$dependencies <- c(private$dependencies, deps_layout)

# DashR's own dependencies
private$dependencies_internal <- dashR:::.dashR_js_metadata()

# return the computed layout
oldClass(layout_) <- c("dash_layout", oldClass(layout_))
layout_
Expand DownExpand Up@@ -676,7 +741,7 @@ Dash <- R6::R6Class(

# akin to https://github.com/plotly/dash-renderer/blob/master/dash_renderer/__init__.py
react_version_enabled= function() {
version <- private$dependencies_internal$react$version
version <- private$dependencies_internal$`react-prod`$version
return(version)
},
react_deps = function() {
Expand All@@ -692,16 +757,32 @@ Dash <- R6::R6Class(
.index = NULL,

collect_resources = function() {
# DashR's own dependencies
# serve the dev version of dash-renderer when in debug mode
dependencies_all_internal <- dashR:::.dashR_js_metadata()
if (private$debug) {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-prod",
"dash-renderer-map-prod")]
} else {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-dev",
"dash-renderer-map-dev")]
}

private$dependencies_internal <- depsSubset

# collect and resolve package dependencies
depsAll <- compact(c(
private$react_deps()[private$react_versions() %in% private$react_version_enabled()],
private$dependencies,
private$dependencies_user,
private$dependencies_internal[names(private$dependencies_internal) %in% 'dash-renderer']
private$dependencies_internal[grepl(pattern = "dash-renderer", x = private$dependencies_internal)]
))

# normalizes local paths and keeps newer versions of duplicates
depsAll <- htmltools::resolveDependencies(depsAll, FALSE)
depsAll <- depsAll[!vapply(depsAll,
function(v) {
!is.null(v[["script"]]) && tools::file_ext(v[["script"]]) == "map"
}, logical(1))]

# styleheets always go in header
css_deps <- compact(lapply(depsAll, function(dep) {
Expand DownExpand Up@@ -732,7 +813,8 @@ Dash <- R6::R6Class(
local = TRUE,
local_path = private$css,
prefix = self$config$requests_pathname_prefix)
} else {
}
else {
css_assets <- NULL
}

Expand DownExpand Up@@ -765,7 +847,13 @@ Dash <- R6::R6Class(
} else {
favicon <- ""
}


# set script tag to invoke a new dash_renderer
scripts_invoke_renderer <- sprintf("<script id=\"%s\" type=\"%s\">%s</script>",
"_dash-renderer",
"application/javascript",
"var renderer = new DashRenderer();")

# serving order of CSS and JS tags: package -> external -> assets
css_tags <- paste(c(css_deps,
css_external,
Expand All@@ -774,7 +862,8 @@ Dash <- R6::R6Class(

scripts_tags <- paste(c(scripts_deps,
scripts_external,
scripts_assets),
scripts_assets,
scripts_invoke_renderer),
collapse = "\n")

return(list(css_tags = css_tags,
Expand DownExpand Up@@ -822,7 +911,6 @@ Dash <- R6::R6Class(
to_JSON(self$config),
scripts_tags
)

}
)
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff9cc4d
Add support for 'subplots' from plot_ly and ggplotly in DashR (#84)
rpkyle Apr 18, 2019
fcb0972
Remove run_heroku() and inspect environment vars for setting host and…
rpkyle Apr 18, 2019
ca533f2
:bell: added warning when file goes unmatched (#81)
rpkyle Apr 22, 2019
689e309
:sparkles: initial support for stack tracing
Apr 23, 2019
cfe9c49
:truck: add dependency on crayon :package:
Apr 27, 2019
897df31
:scissors: trim call stack from top also
Apr 27, 2019
0ad3b38
renamed stack trace fn
Apr 27, 2019
0d526b4
:sparkles: capture errors as warnings in debug mode
Apr 27, 2019
6291553
Merge branch '0.0.7-dev' into 0.0.7-debug
rpkyle Apr 28, 2019
d84b964
:sparkles: initial support for dash-renderer v23
May 4, 2019
0371634
resolved conflicts
May 4, 2019
73a429a
:bug: fix missing parenthesis
May 4, 2019
35dedac
rename pruned to pruned_errors
May 4, 2019
53b70dd
rename pruned to pruned_errors in utils.R also
May 4, 2019
151e838
:sparkles: add options for dev_tools_ui and dev_tools_props_check
May 4, 2019
140bf51
:sparkles: initial support for callback context
May 17, 2019
dd679f6
:hammer: deps handling changes to better support CSS
Jun 6, 2019
f521fd4
Improvements to debugging in Dash for R (#87)
rpkyle Jun 13, 2019
d9d66c1
:camel: fixes, :cow: fixes
Jun 13, 2019
f10e7ae
:camel: fixes
Jun 13, 2019
2fb324e
:necktie: remove stray parenthesis
Jun 13, 2019
dee5b71
Merge branch '0.0.7-dev' into 0.0.7-debug-CSS
rpkyle Jun 14, 2019
b4ab120
modify tag generation to allow modtimes
Jun 14, 2019
ba8ecf3
:hocho: callback_context initialization code
rpkyle Jun 17, 2019
006d3e7
Merge branch '0.1.0-dev' into 0.0.7-debug-CSS
rpkyle Jun 18, 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
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,8 @@ Imports:
assertthat,
digest,
base64enc,
mime
mime,
crayon
Suggests:
dashHtmlComponents (>= 0.16.0),
dashCoreComponents (>= 0.48.0),
Expand Down
208 changes: 148 additions & 60 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,16 +69,13 @@
#' provide [input] (and/or [state]) object(s) (which should reference
#' layout components) as argument value(s) to `func`.
#' }
#' \item{`run_server(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...)`}{
#' \item{`run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' Launch the application. If provided, `host`/`port` set
#' the `host`/`port` fields of the underlying [fiery::Fire] web
#' server. The `block`/`showcase`/`...` arguments are passed along
#' to the `ignite()` method of the [fiery::Fire] server.
#' }
#' \item{`run_heroku(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...)`}{
#' Like `run_server()` but sets sensible `host`/`port` defaults
#' for running the app on Heroku.
#' }
#' }
#'
#' @export
Expand DownExpand Up@@ -124,6 +121,7 @@ Dash <- R6::R6Class(
private$assets_url_path <- sub("/$", "", assets_url_path)
private$assets_ignore <- assets_ignore
private$suppress_callback_exceptions <- suppress_callback_exceptions

# config options
self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX")
self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX")
Expand DownExpand Up@@ -196,8 +194,8 @@ Dash <- R6::R6Class(

payload <- Map(function(callback_signature) {
list(
output=callback_signature$output,
inputs=callback_signature$inputs,
output=paste0(callback_signature$output, collapse="."),
state=callback_signature$state
)
}, private$callback_map)
Expand All@@ -220,8 +218,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]][['func']]
callback <- private$callback_map[[request$body$output]][['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 DownExpand Up@@ -257,24 +254,45 @@ Dash <- R6::R6Class(
}
}

output_value <- do.call(callback, callback_args)

# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), request$body$output$property)
# set the callback context associated with this invocation of the callback
private$callback_context_ <- setCallbackContext(request$body)

output_value <- getStackTrace(do.call(callback, callback_args),
debug = private$debug,
pruned_errors = private$pruned_errors)

# reset callback context
private$callback_context_ <- NULL

if (is.null(private$stack_message)) {
# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), gsub( "(^.+)(\\.)", "", request$body$output))
)
)
)

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'
} else if (private$debug==TRUE) {
# if there is an error, send it back to dash-renderer
response$body <- private$stack_message
response$status <- 500L
response$type <- 'html'
private$stack_message <- NULL
} else {
# if not in debug mode, do not return stack
response$body <- NULL
response$status <- 500L
private$stack_message <- NULL
}
TRUE
})

Expand All@@ -283,9 +301,9 @@ Dash <- R6::R6Class(
# https://github.com/plotly/dash/blob/1249ffbd051bfb5fdbe439612cbec7fa8fff5ab5/dash/dash.py#L488
# https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data
dash_suite <- paste0(self$config$routes_pathname_prefix, "_dash-component-suites/:package_name/:filename")
route$add_handler("get", dash_suite, function(request, response, keys, ...) {

route$add_handler("get", dash_suite, function(request, response, keys, ...) {
filename <- basename(file.path(keys$filename))

dep_list <- c(private$dependencies_internal,
private$dependencies,
private$dependencies_user)
Expand All@@ -295,18 +313,31 @@ Dash <- R6::R6Class(
clean_dependencies(dep_list)
)

dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
# return warning if a dependency goes unmatched, since the page
# will probably fail to render properly anyway without it
if (length(dep_pkg$rpkg_path) == 0) {
warning(sprintf("The dependency '%s' could not be loaded; the file was not found.",
filename),
call. = FALSE)

response$body <- NULL
response$status <- 404L
} else {
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
}

TRUE
})

Expand DownExpand Up@@ -443,27 +474,56 @@ Dash <- R6::R6Class(

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

# ------------------------------------------------------------------------
# request and return callback context
# ------------------------------------------------------------------------
callback_context = function() {
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context may only be accessed within a callback.")
}
private$callback_context_
},

# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...) {
if (!is.null(host)) self$server$host <- host
if (!is.null(port)) self$server$port <- as.numeric(port)
self$server$ignite(block = block, showcase = showcase, ...)
},
run_heroku = function(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...) {
run_server = function(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
port = Sys.getenv('DASH_PORT', 8050),
block = TRUE,
showcase = FALSE,
pruned_errors = TRUE,
debug = FALSE,
dev_tools_ui = NULL,
dev_tools_props_check = NULL,
...) {
self$server$host <- host
self$server$port <- as.numeric(port)
self$run_server(...)
}
),

if (debug & !(isFALSE(dev_tools_ui)) | isTRUE(dev_tools_ui)) {
self$config$ui <- TRUE
} else {
self$config$ui <- FALSE
}

if (debug & !(isFALSE(dev_tools_props_check)) | isTRUE(dev_tools_props_check)) {
self$config$props_check <- TRUE
} else {
self$config$props_check <- FALSE
}

private$pruned_errors <- pruned_errors
private$debug <- debug

self$server$ignite(block = block, showcase = showcase, ...)
}
),

private = list(
# private fields defined on initiation
Expand All@@ -479,7 +539,15 @@ Dash <- R6::R6Class(
css = NULL,
scripts = NULL,
other = NULL,


# initialize flags for debug mode and stack pruning,
debug = NULL,
pruned_errors = NULL,
stack_message = NULL,

# callback context
callback_context_ = NULL,

# fields for tracking HTML dependencies
dependencies = list(),
dependencies_user = list(),
Expand DownExpand Up@@ -566,9 +634,6 @@ Dash <- R6::R6Class(
# add on HTML dependencies we've identified by crawling the layout
private$dependencies <- c(private$dependencies, deps_layout)

# DashR's own dependencies
private$dependencies_internal <- dashR:::.dashR_js_metadata()

# return the computed layout
oldClass(layout_) <- c("dash_layout", oldClass(layout_))
layout_
Expand DownExpand Up@@ -676,7 +741,7 @@ Dash <- R6::R6Class(

# akin to https://github.com/plotly/dash-renderer/blob/master/dash_renderer/__init__.py
react_version_enabled= function() {
version <- private$dependencies_internal$react$version
version <- private$dependencies_internal$`react-prod`$version
return(version)
},
react_deps = function() {
Expand All@@ -692,16 +757,32 @@ Dash <- R6::R6Class(
.index = NULL,

collect_resources = function() {
# DashR's own dependencies
# serve the dev version of dash-renderer when in debug mode
dependencies_all_internal <- dashR:::.dashR_js_metadata()
if (private$debug) {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-prod",
"dash-renderer-map-prod")]
} else {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-dev",
"dash-renderer-map-dev")]
}

private$dependencies_internal <- depsSubset

# collect and resolve package dependencies
depsAll <- compact(c(
private$react_deps()[private$react_versions() %in% private$react_version_enabled()],
private$dependencies,
private$dependencies_user,
private$dependencies_internal[names(private$dependencies_internal) %in% 'dash-renderer']
private$dependencies_internal[grepl(pattern = "dash-renderer", x = private$dependencies_internal)]
))

# normalizes local paths and keeps newer versions of duplicates
depsAll <- htmltools::resolveDependencies(depsAll, FALSE)
depsAll <- depsAll[!vapply(depsAll,
function(v) {
!is.null(v[["script"]]) && tools::file_ext(v[["script"]]) == "map"
}, logical(1))]

# styleheets always go in header
css_deps <- compact(lapply(depsAll, function(dep) {
Expand DownExpand Up@@ -732,7 +813,8 @@ Dash <- R6::R6Class(
local = TRUE,
local_path = private$css,
prefix = self$config$requests_pathname_prefix)
} else {
}
else {
css_assets <- NULL
}

Expand DownExpand Up@@ -765,7 +847,13 @@ Dash <- R6::R6Class(
} else {
favicon <- ""
}


# set script tag to invoke a new dash_renderer
scripts_invoke_renderer <- sprintf("<script id=\"%s\" type=\"%s\">%s</script>",
"_dash-renderer",
"application/javascript",
"var renderer = new DashRenderer();")

# serving order of CSS and JS tags: package -> external -> assets
css_tags <- paste(c(css_deps,
css_external,
Expand All@@ -774,7 +862,8 @@ Dash <- R6::R6Class(

scripts_tags <- paste(c(scripts_deps,
scripts_external,
scripts_assets),
scripts_assets,
scripts_invoke_renderer),
collapse = "\n")

return(list(css_tags = css_tags,
Expand DownExpand Up@@ -822,7 +911,6 @@ Dash <- R6::R6Class(
to_JSON(self$config),
scripts_tags
)

}
)
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff9cc4d
Add support for 'subplots' from plot_ly and ggplotly in DashR (#84)
rpkyle Apr 18, 2019
fcb0972
Remove run_heroku() and inspect environment vars for setting host and…
rpkyle Apr 18, 2019
ca533f2
:bell: added warning when file goes unmatched (#81)
rpkyle Apr 22, 2019
689e309
:sparkles: initial support for stack tracing
Apr 23, 2019
cfe9c49
:truck: add dependency on crayon :package:
Apr 27, 2019
897df31
:scissors: trim call stack from top also
Apr 27, 2019
0ad3b38
renamed stack trace fn
Apr 27, 2019
0d526b4
:sparkles: capture errors as warnings in debug mode
Apr 27, 2019
6291553
Merge branch '0.0.7-dev' into 0.0.7-debug
rpkyle Apr 28, 2019
d84b964
:sparkles: initial support for dash-renderer v23
May 4, 2019
0371634
resolved conflicts
May 4, 2019
73a429a
:bug: fix missing parenthesis
May 4, 2019
35dedac
rename pruned to pruned_errors
May 4, 2019
53b70dd
rename pruned to pruned_errors in utils.R also
May 4, 2019
151e838
:sparkles: add options for dev_tools_ui and dev_tools_props_check
May 4, 2019
140bf51
:sparkles: initial support for callback context
May 17, 2019
dd679f6
:hammer: deps handling changes to better support CSS
Jun 6, 2019
f521fd4
Improvements to debugging in Dash for R (#87)
rpkyle Jun 13, 2019
d9d66c1
:camel: fixes, :cow: fixes
Jun 13, 2019
f10e7ae
:camel: fixes
Jun 13, 2019
2fb324e
:necktie: remove stray parenthesis
Jun 13, 2019
dee5b71
Merge branch '0.0.7-dev' into 0.0.7-debug-CSS
rpkyle Jun 14, 2019
b4ab120
modify tag generation to allow modtimes
Jun 14, 2019
ba8ecf3
:hocho: callback_context initialization code
rpkyle Jun 17, 2019
006d3e7
Merge branch '0.1.0-dev' into 0.0.7-debug-CSS
rpkyle Jun 18, 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
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,8 @@ Imports:
assertthat,
digest,
base64enc,
mime
mime,
crayon
Suggests:
dashHtmlComponents (>= 0.16.0),
dashCoreComponents (>= 0.48.0),
Expand Down
208 changes: 148 additions & 60 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,16 +69,13 @@
#' provide [input] (and/or [state]) object(s) (which should reference
#' layout components) as argument value(s) to `func`.
#' }
#' \item{`run_server(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...)`}{
#' \item{`run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' Launch the application. If provided, `host`/`port` set
#' the `host`/`port` fields of the underlying [fiery::Fire] web
#' server. The `block`/`showcase`/`...` arguments are passed along
#' to the `ignite()` method of the [fiery::Fire] server.
#' }
#' \item{`run_heroku(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...)`}{
#' Like `run_server()` but sets sensible `host`/`port` defaults
#' for running the app on Heroku.
#' }
#' }
#'
#' @export
Expand DownExpand Up@@ -124,6 +121,7 @@ Dash <- R6::R6Class(
private$assets_url_path <- sub("/$", "", assets_url_path)
private$assets_ignore <- assets_ignore
private$suppress_callback_exceptions <- suppress_callback_exceptions

# config options
self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX")
self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX")
Expand DownExpand Up@@ -196,8 +194,8 @@ Dash <- R6::R6Class(

payload <- Map(function(callback_signature) {
list(
output=callback_signature$output,
inputs=callback_signature$inputs,
output=paste0(callback_signature$output, collapse="."),
state=callback_signature$state
)
}, private$callback_map)
Expand All@@ -220,8 +218,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]][['func']]
callback <- private$callback_map[[request$body$output]][['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 DownExpand Up@@ -257,24 +254,45 @@ Dash <- R6::R6Class(
}
}

output_value <- do.call(callback, callback_args)

# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), request$body$output$property)
# set the callback context associated with this invocation of the callback
private$callback_context_ <- setCallbackContext(request$body)

output_value <- getStackTrace(do.call(callback, callback_args),
debug = private$debug,
pruned_errors = private$pruned_errors)

# reset callback context
private$callback_context_ <- NULL

if (is.null(private$stack_message)) {
# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), gsub( "(^.+)(\\.)", "", request$body$output))
)
)
)

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'
} else if (private$debug==TRUE) {
# if there is an error, send it back to dash-renderer
response$body <- private$stack_message
response$status <- 500L
response$type <- 'html'
private$stack_message <- NULL
} else {
# if not in debug mode, do not return stack
response$body <- NULL
response$status <- 500L
private$stack_message <- NULL
}
TRUE
})

Expand All@@ -283,9 +301,9 @@ Dash <- R6::R6Class(
# https://github.com/plotly/dash/blob/1249ffbd051bfb5fdbe439612cbec7fa8fff5ab5/dash/dash.py#L488
# https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data
dash_suite <- paste0(self$config$routes_pathname_prefix, "_dash-component-suites/:package_name/:filename")
route$add_handler("get", dash_suite, function(request, response, keys, ...) {

route$add_handler("get", dash_suite, function(request, response, keys, ...) {
filename <- basename(file.path(keys$filename))

dep_list <- c(private$dependencies_internal,
private$dependencies,
private$dependencies_user)
Expand All@@ -295,18 +313,31 @@ Dash <- R6::R6Class(
clean_dependencies(dep_list)
)

dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
# return warning if a dependency goes unmatched, since the page
# will probably fail to render properly anyway without it
if (length(dep_pkg$rpkg_path) == 0) {
warning(sprintf("The dependency '%s' could not be loaded; the file was not found.",
filename),
call. = FALSE)

response$body <- NULL
response$status <- 404L
} else {
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
}

TRUE
})

Expand DownExpand Up@@ -443,27 +474,56 @@ Dash <- R6::R6Class(

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

# ------------------------------------------------------------------------
# request and return callback context
# ------------------------------------------------------------------------
callback_context = function() {
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context may only be accessed within a callback.")
}
private$callback_context_
},

# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...) {
if (!is.null(host)) self$server$host <- host
if (!is.null(port)) self$server$port <- as.numeric(port)
self$server$ignite(block = block, showcase = showcase, ...)
},
run_heroku = function(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...) {
run_server = function(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
port = Sys.getenv('DASH_PORT', 8050),
block = TRUE,
showcase = FALSE,
pruned_errors = TRUE,
debug = FALSE,
dev_tools_ui = NULL,
dev_tools_props_check = NULL,
...) {
self$server$host <- host
self$server$port <- as.numeric(port)
self$run_server(...)
}
),

if (debug & !(isFALSE(dev_tools_ui)) | isTRUE(dev_tools_ui)) {
self$config$ui <- TRUE
} else {
self$config$ui <- FALSE
}

if (debug & !(isFALSE(dev_tools_props_check)) | isTRUE(dev_tools_props_check)) {
self$config$props_check <- TRUE
} else {
self$config$props_check <- FALSE
}

private$pruned_errors <- pruned_errors
private$debug <- debug

self$server$ignite(block = block, showcase = showcase, ...)
}
),

private = list(
# private fields defined on initiation
Expand All@@ -479,7 +539,15 @@ Dash <- R6::R6Class(
css = NULL,
scripts = NULL,
other = NULL,


# initialize flags for debug mode and stack pruning,
debug = NULL,
pruned_errors = NULL,
stack_message = NULL,

# callback context
callback_context_ = NULL,

# fields for tracking HTML dependencies
dependencies = list(),
dependencies_user = list(),
Expand DownExpand Up@@ -566,9 +634,6 @@ Dash <- R6::R6Class(
# add on HTML dependencies we've identified by crawling the layout
private$dependencies <- c(private$dependencies, deps_layout)

# DashR's own dependencies
private$dependencies_internal <- dashR:::.dashR_js_metadata()

# return the computed layout
oldClass(layout_) <- c("dash_layout", oldClass(layout_))
layout_
Expand DownExpand Up@@ -676,7 +741,7 @@ Dash <- R6::R6Class(

# akin to https://github.com/plotly/dash-renderer/blob/master/dash_renderer/__init__.py
react_version_enabled= function() {
version <- private$dependencies_internal$react$version
version <- private$dependencies_internal$`react-prod`$version
return(version)
},
react_deps = function() {
Expand All@@ -692,16 +757,32 @@ Dash <- R6::R6Class(
.index = NULL,

collect_resources = function() {
# DashR's own dependencies
# serve the dev version of dash-renderer when in debug mode
dependencies_all_internal <- dashR:::.dashR_js_metadata()
if (private$debug) {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-prod",
"dash-renderer-map-prod")]
} else {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-dev",
"dash-renderer-map-dev")]
}

private$dependencies_internal <- depsSubset

# collect and resolve package dependencies
depsAll <- compact(c(
private$react_deps()[private$react_versions() %in% private$react_version_enabled()],
private$dependencies,
private$dependencies_user,
private$dependencies_internal[names(private$dependencies_internal) %in% 'dash-renderer']
private$dependencies_internal[grepl(pattern = "dash-renderer", x = private$dependencies_internal)]
))

# normalizes local paths and keeps newer versions of duplicates
depsAll <- htmltools::resolveDependencies(depsAll, FALSE)
depsAll <- depsAll[!vapply(depsAll,
function(v) {
!is.null(v[["script"]]) && tools::file_ext(v[["script"]]) == "map"
}, logical(1))]

# styleheets always go in header
css_deps <- compact(lapply(depsAll, function(dep) {
Expand DownExpand Up@@ -732,7 +813,8 @@ Dash <- R6::R6Class(
local = TRUE,
local_path = private$css,
prefix = self$config$requests_pathname_prefix)
} else {
}
else {
css_assets <- NULL
}

Expand DownExpand Up@@ -765,7 +847,13 @@ Dash <- R6::R6Class(
} else {
favicon <- ""
}


# set script tag to invoke a new dash_renderer
scripts_invoke_renderer <- sprintf("<script id=\"%s\" type=\"%s\">%s</script>",
"_dash-renderer",
"application/javascript",
"var renderer = new DashRenderer();")

# serving order of CSS and JS tags: package -> external -> assets
css_tags <- paste(c(css_deps,
css_external,
Expand All@@ -774,7 +862,8 @@ Dash <- R6::R6Class(

scripts_tags <- paste(c(scripts_deps,
scripts_external,
scripts_assets),
scripts_assets,
scripts_invoke_renderer),
collapse = "\n")

return(list(css_tags = css_tags,
Expand DownExpand Up@@ -822,7 +911,6 @@ Dash <- R6::R6Class(
to_JSON(self$config),
scripts_tags
)

}
)
)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ff9cc4d
Add support for 'subplots' from plot_ly and ggplotly in DashR (#84)
rpkyle Apr 18, 2019
fcb0972
Remove run_heroku() and inspect environment vars for setting host and…
rpkyle Apr 18, 2019
ca533f2
:bell: added warning when file goes unmatched (#81)
rpkyle Apr 22, 2019
689e309
:sparkles: initial support for stack tracing
Apr 23, 2019
cfe9c49
:truck: add dependency on crayon :package:
Apr 27, 2019
897df31
:scissors: trim call stack from top also
Apr 27, 2019
0ad3b38
renamed stack trace fn
Apr 27, 2019
0d526b4
:sparkles: capture errors as warnings in debug mode
Apr 27, 2019
6291553
Merge branch '0.0.7-dev' into 0.0.7-debug
rpkyle Apr 28, 2019
d84b964
:sparkles: initial support for dash-renderer v23
May 4, 2019
0371634
resolved conflicts
May 4, 2019
73a429a
:bug: fix missing parenthesis
May 4, 2019
35dedac
rename pruned to pruned_errors
May 4, 2019
53b70dd
rename pruned to pruned_errors in utils.R also
May 4, 2019
151e838
:sparkles: add options for dev_tools_ui and dev_tools_props_check
May 4, 2019
140bf51
:sparkles: initial support for callback context
May 17, 2019
dd679f6
:hammer: deps handling changes to better support CSS
Jun 6, 2019
f521fd4
Improvements to debugging in Dash for R (#87)
rpkyle Jun 13, 2019
d9d66c1
:camel: fixes, :cow: fixes
Jun 13, 2019
f10e7ae
:camel: fixes
Jun 13, 2019
2fb324e
:necktie: remove stray parenthesis
Jun 13, 2019
dee5b71
Merge branch '0.0.7-dev' into 0.0.7-debug-CSS
rpkyle Jun 14, 2019
b4ab120
modify tag generation to allow modtimes
Jun 14, 2019
ba8ecf3
:hocho: callback_context initialization code
rpkyle Jun 17, 2019
006d3e7
Merge branch '0.1.0-dev' into 0.0.7-debug-CSS
rpkyle Jun 18, 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
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,8 @@ Imports:
assertthat,
digest,
base64enc,
mime
mime,
crayon
Suggests:
dashHtmlComponents (>= 0.16.0),
dashCoreComponents (>= 0.48.0),
Expand Down
208 changes: 148 additions & 60 deletions R/dash.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,16 +69,13 @@
#' provide [input] (and/or [state]) object(s) (which should reference
#' layout components) as argument value(s) to `func`.
#' }
#' \item{`run_server(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...)`}{
#' \item{`run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' Launch the application. If provided, `host`/`port` set
#' the `host`/`port` fields of the underlying [fiery::Fire] web
#' server. The `block`/`showcase`/`...` arguments are passed along
#' to the `ignite()` method of the [fiery::Fire] server.
#' }
#' \item{`run_heroku(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...)`}{
#' Like `run_server()` but sets sensible `host`/`port` defaults
#' for running the app on Heroku.
#' }
#' }
#'
#' @export
Expand DownExpand Up@@ -124,6 +121,7 @@ Dash <- R6::R6Class(
private$assets_url_path <- sub("/$", "", assets_url_path)
private$assets_ignore <- assets_ignore
private$suppress_callback_exceptions <- suppress_callback_exceptions

# config options
self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX")
self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX")
Expand DownExpand Up@@ -196,8 +194,8 @@ Dash <- R6::R6Class(

payload <- Map(function(callback_signature) {
list(
output=callback_signature$output,
inputs=callback_signature$inputs,
output=paste0(callback_signature$output, collapse="."),
state=callback_signature$state
)
}, private$callback_map)
Expand All@@ -220,8 +218,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]][['func']]
callback <- private$callback_map[[request$body$output]][['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 DownExpand Up@@ -257,24 +254,45 @@ Dash <- R6::R6Class(
}
}

output_value <- do.call(callback, callback_args)

# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), request$body$output$property)
# set the callback context associated with this invocation of the callback
private$callback_context_ <- setCallbackContext(request$body)

output_value <- getStackTrace(do.call(callback, callback_args),
debug = private$debug,
pruned_errors = private$pruned_errors)

# reset callback context
private$callback_context_ <- NULL

if (is.null(private$stack_message)) {
# pass on output_value to encode_plotly in case there are dccGraph
# components which include Plotly.js figures for which we'll need to
# run plotly_build from the plotly package
output_value <- encode_plotly(output_value)

# have to format the response body like this
# https://github.com/plotly/dash/blob/064c811d/dash/dash.py#L562-L584
resp <- list(
response = list(
props = setNames(list(output_value), gsub( "(^.+)(\\.)", "", request$body$output))
)
)
)

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'

response$body <- to_JSON(resp)
response$status <- 200L
response$type <- 'json'
} else if (private$debug==TRUE) {
# if there is an error, send it back to dash-renderer
response$body <- private$stack_message
response$status <- 500L
response$type <- 'html'
private$stack_message <- NULL
} else {
# if not in debug mode, do not return stack
response$body <- NULL
response$status <- 500L
private$stack_message <- NULL
}
TRUE
})

Expand All@@ -283,9 +301,9 @@ Dash <- R6::R6Class(
# https://github.com/plotly/dash/blob/1249ffbd051bfb5fdbe439612cbec7fa8fff5ab5/dash/dash.py#L488
# https://docs.python.org/3/library/pkgutil.html#pkgutil.get_data
dash_suite <- paste0(self$config$routes_pathname_prefix, "_dash-component-suites/:package_name/:filename")
route$add_handler("get", dash_suite, function(request, response, keys, ...) {

route$add_handler("get", dash_suite, function(request, response, keys, ...) {
filename <- basename(file.path(keys$filename))

dep_list <- c(private$dependencies_internal,
private$dependencies,
private$dependencies_user)
Expand All@@ -295,18 +313,31 @@ Dash <- R6::R6Class(
clean_dependencies(dep_list)
)

dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
# return warning if a dependency goes unmatched, since the page
# will probably fail to render properly anyway without it
if (length(dep_pkg$rpkg_path) == 0) {
warning(sprintf("The dependency '%s' could not be loaded; the file was not found.",
filename),
call. = FALSE)

response$body <- NULL
response$status <- 404L
} else {
dep_path <- system.file(dep_pkg$rpkg_path,
package = dep_pkg$rpkg_name)

response$body <- readLines(dep_path,
warn = FALSE,
encoding = "UTF-8")
response$status <- 200L
response$set_header('Cache-Control',
sprintf('public, max-age=%s',
components_cache_max_age)
)
response$type <- get_mimetype(filename)
}

TRUE
})

Expand DownExpand Up@@ -443,27 +474,56 @@ Dash <- R6::R6Class(

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

# ------------------------------------------------------------------------
# request and return callback context
# ------------------------------------------------------------------------
callback_context = function() {
if (is.null(private$callback_context_)) {
warning("callback_context is undefined; callback_context may only be accessed within a callback.")
}
private$callback_context_
},

# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = NULL, port = NULL, block = TRUE, showcase = FALSE, ...) {
if (!is.null(host)) self$server$host <- host
if (!is.null(port)) self$server$port <- as.numeric(port)
self$server$ignite(block = block, showcase = showcase, ...)
},
run_heroku = function(host = "0.0.0.0", port = Sys.getenv('PORT', 8080), ...) {
run_server = function(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
port = Sys.getenv('DASH_PORT', 8050),
block = TRUE,
showcase = FALSE,
pruned_errors = TRUE,
debug = FALSE,
dev_tools_ui = NULL,
dev_tools_props_check = NULL,
...) {
self$server$host <- host
self$server$port <- as.numeric(port)
self$run_server(...)
}
),

if (debug & !(isFALSE(dev_tools_ui)) | isTRUE(dev_tools_ui)) {
self$config$ui <- TRUE
} else {
self$config$ui <- FALSE
}

if (debug & !(isFALSE(dev_tools_props_check)) | isTRUE(dev_tools_props_check)) {
self$config$props_check <- TRUE
} else {
self$config$props_check <- FALSE
}

private$pruned_errors <- pruned_errors
private$debug <- debug

self$server$ignite(block = block, showcase = showcase, ...)
}
),

private = list(
# private fields defined on initiation
Expand All@@ -479,7 +539,15 @@ Dash <- R6::R6Class(
css = NULL,
scripts = NULL,
other = NULL,


# initialize flags for debug mode and stack pruning,
debug = NULL,
pruned_errors = NULL,
stack_message = NULL,

# callback context
callback_context_ = NULL,

# fields for tracking HTML dependencies
dependencies = list(),
dependencies_user = list(),
Expand DownExpand Up@@ -566,9 +634,6 @@ Dash <- R6::R6Class(
# add on HTML dependencies we've identified by crawling the layout
private$dependencies <- c(private$dependencies, deps_layout)

# DashR's own dependencies
private$dependencies_internal <- dashR:::.dashR_js_metadata()

# return the computed layout
oldClass(layout_) <- c("dash_layout", oldClass(layout_))
layout_
Expand DownExpand Up@@ -676,7 +741,7 @@ Dash <- R6::R6Class(

# akin to https://github.com/plotly/dash-renderer/blob/master/dash_renderer/__init__.py
react_version_enabled= function() {
version <- private$dependencies_internal$react$version
version <- private$dependencies_internal$`react-prod`$version
return(version)
},
react_deps = function() {
Expand All@@ -692,16 +757,32 @@ Dash <- R6::R6Class(
.index = NULL,

collect_resources = function() {
# DashR's own dependencies
# serve the dev version of dash-renderer when in debug mode
dependencies_all_internal <- dashR:::.dashR_js_metadata()
if (private$debug) {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-prod",
"dash-renderer-map-prod")]
} else {
depsSubset <- dependencies_all_internal[names(dependencies_all_internal) != c("dash-renderer-dev",
"dash-renderer-map-dev")]
}

private$dependencies_internal <- depsSubset

# collect and resolve package dependencies
depsAll <- compact(c(
private$react_deps()[private$react_versions() %in% private$react_version_enabled()],
private$dependencies,
private$dependencies_user,
private$dependencies_internal[names(private$dependencies_internal) %in% 'dash-renderer']
private$dependencies_internal[grepl(pattern = "dash-renderer", x = private$dependencies_internal)]
))

# normalizes local paths and keeps newer versions of duplicates
depsAll <- htmltools::resolveDependencies(depsAll, FALSE)
depsAll <- depsAll[!vapply(depsAll,
function(v) {
!is.null(v[["script"]]) && tools::file_ext(v[["script"]]) == "map"
}, logical(1))]

# styleheets always go in header
css_deps <- compact(lapply(depsAll, function(dep) {
Expand DownExpand Up@@ -732,7 +813,8 @@ Dash <- R6::R6Class(
local = TRUE,
local_path = private$css,
prefix = self$config$requests_pathname_prefix)
} else {
}
else {
css_assets <- NULL
}

Expand DownExpand Up@@ -765,7 +847,13 @@ Dash <- R6::R6Class(
} else {
favicon <- ""
}


# set script tag to invoke a new dash_renderer
scripts_invoke_renderer <- sprintf("<script id=\"%s\" type=\"%s\">%s</script>",
"_dash-renderer",
"application/javascript",
"var renderer = new DashRenderer();")

# serving order of CSS and JS tags: package -> external -> assets
css_tags <- paste(c(css_deps,
css_external,
Expand All@@ -774,7 +862,8 @@ Dash <- R6::R6Class(

scripts_tags <- paste(c(scripts_deps,
scripts_external,
scripts_assets),
scripts_assets,
scripts_invoke_renderer),
collapse = "\n")

return(list(css_tags = css_tags,
Expand DownExpand Up@@ -822,7 +911,6 @@ Dash <- R6::R6Class(
to_JSON(self$config),
scripts_tags
)

}
)
)
Expand Down
Loading