Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support for index page templating #168

Merged
merged 35 commits into from
Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f6c13a9
Dash for R v0.2.0 (#162)
rpkyle Jan 4, 2020
f81fcb5
Update README.md
rpkyle Jan 5, 2020
a58d4f2
Added initial implementation of index customization with strings
HammadTheOne Jan 9, 2020
4d72243
Fixed issue with `collect_assets` being undefined
HammadTheOne Jan 10, 2020
30251cf
Added description and details.
HammadTheOne Jan 10, 2020
3346a93
Added description
HammadTheOne Jan 14, 2020
2e58b37
Added error messages and warnings.
HammadTheOne Jan 14, 2020
0984660
Merge branch 'dev' into 42-index-page-templating
HammadTheOne Jan 14, 2020
3aa0f50
Updated version to 0.3.0
HammadTheOne Jan 16, 2020
a2410d5
Added CRLF
HammadTheOne Jan 16, 2020
a158a37
Updated environment variable names to `dev` versions.
HammadTheOne Jan 17, 2020
1138e4c
Improved error message function, made parity changes for index keys.
HammadTheOne Jan 17, 2020
fd67f8a
Added available keys to description.
HammadTheOne Jan 17, 2020
17a61d1
Parity changes to `app_entry` and `config` keys
HammadTheOne Jan 17, 2020
13ebec4
Cleaned up description.
HammadTheOne Jan 17, 2020
ab54aa0
Added `interpolate_str` helper function to `utils.R`
HammadTheOne Jan 17, 2020
ce4a8b6
Changes to default index, consolidating `_dash-config`
HammadTheOne Jan 17, 2020
036e4e5
Added template index and logic for choosing correct index.
HammadTheOne Jan 19, 2020
09b0851
Added description for `interpolate_index`.
HammadTheOne Jan 19, 2020
4023b67
Updated glue arguments in `index_string` method, added example index.
HammadTheOne Jan 27, 2020
8e98370
Update to `interpolate_index` to reflect changes to `index_string`.
HammadTheOne Jan 27, 2020
0ce3158
Bugfix, referenced correct private variable.
HammadTheOne Jan 27, 2020
7fd1ebe
Update R/dash.R
rpkyle Feb 6, 2020
a0c2f7e
:hocho: cat(template)
Feb 6, 2020
5ae7549
:camel: make concise
Feb 6, 2020
79b0faa
replace = with <-
Feb 6, 2020
39d52e0
:dromedary_camel: DRY up key validation logic
Feb 6, 2020
8b71875
:black_large_square: use invisible
Feb 6, 2020
f3dbfb9
use latest pkgs
Feb 11, 2020
e3a40c3
update package docs
Feb 11, 2020
df3d780
update docs; remove name, add title
Feb 11, 2020
84f8464
note title method
Feb 11, 2020
5bbba6e
:see_no_evil: need default app name
Feb 11, 2020
2aef1f3
:rotating_light: add integration tests
Feb 11, 2020
56904ba
fix ref for dcc
Feb 11, 2020
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
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: dash
Title: An Interface to the Dash Ecosystem for Authoring Reactive Web Applications
Version: 0.2.0
Version: 0.3.0
Authors@R: c(person("Chris", "Parmer", role = c("aut"), email = "chris@plot.ly"), person("Ryan Patrick", "Kyle", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-5829-9867"), email = "ryan@plot.ly"), person("Carson", "Sievert", role = c("aut"), comment = c(ORCID = "0000-0002-4958-2844")), person(family = "Plotly Technologies", role = "cph"))
Description: A framework for building analytical web applications, Dash offers a pleasant and productive development experience. No JavaScript required.
Depends:
Expand Down
3 changes: 2 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ importFrom(routr,RouteStack)
importFrom(routr,ressource_route)
importFrom(stats,setNames)
importFrom(tools,file_ext)
importFrom(utils,getFromNamespace)
importFrom(utils,getFromNamespace)

155 changes: 139 additions & 16 deletions R/dash.R
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,55 @@
#' present a warning and return `NULL` if the Dash app was not loaded via `source()`
#' if the `DASH_APP_PATH` environment variable is undefined.
#' }
#' \item{`run_server(host = Sys.getenv('HOST', "127.0.0.1"),
#' \item{`index_string(string)`}{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

#' The `index_string` method allows the specification of a custom index by changing
rpkyle marked this conversation as resolved.
Show resolved Hide resolved
#' the default `HTML` template that is generated by the Dash UI. Meta tags, CSS, Javascript,
#' are some examples of features that can be modified..This method will present a warning if your
#' HTML template is missing any necessary components and return an error if a valid index is not defined
#' {%metas%} # optional - The registered meta tags.
#' {%favicon%} # optional - A favicon link tag if found in `assets`.
#' {%css%} # optional - link tags to css resources.
#' {%config%} # required - Config generated by dash for the renderer.
#' {%app_entry%} # required - The container where dash react components are rendered.
#' {%scripts%} # required - Collected dependencies scripts tags.
#'
#' Example of a basic html index:
#' "<!DOCTYPE html>
#' <html>
#' <head>
#' {%meta_tags%}
#' <title>{private$name}</title>
#' {%favicon%}
#' {%css_tags%}
#' </head>
#' <body>
#' {%app_entry%}
#' <footer>
#' {%config%}
#' {%scripts%}
#' </footer>
#' </body>
#' </html>"
#'
#' \describe{
#' \item{string}{Character. A formatted string with a complete HTML index}
#' }
#' }
#' \item{`interpolate_index(template_index, ...)`}{
#' With the `interpolate_index` method, we can pass a custom index with template string
#' variables that are already evaluated. We can directly pass arguments to the `template_index`
#' by assigning them to variables present in the template. This is similar to the `index_string` method
#' but offers the ability to change the default components of the Dash index as seen in the example below:
#'
#' """
#' app$interpolate_index(template_index, metas = "<meta_charset='UTF-8'/>", renderer = renderer, config = config)
#' \describe{
#' \item{template_index}{Character. A formatted string with the HTML index string. Defaults to the initial template}
#' \item{unnamed arguments}{Named List. These can be passed as individual named lists corresponding to the components
#' of the Dash html index. These include the same arguments as those found in the `index_string()` template.}
#' }
#' }
#' \item{`run_server(host = Sys.getenv('HOST', "127.0.0.1"),
#' port = Sys.getenv('PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
#' The `run_server` method has 13 formal arguments, several of which are optional:
#' \describe{
Expand Down Expand Up @@ -763,11 +811,53 @@ Dash <- R6::R6Class(
sep="/")))
},

# ------------------------------------------------------------------------
# specify a custom index string
# ------------------------------------------------------------------------
index_string = function(string) {
requiredKeys <- c("app_entry", "config", "scripts")

checks <- sapply(requiredKeys, function(x) grepl(x, string))

if (FALSE %in% checks) {
stop(sprintf("Did you forget to include %s in your index string?",
paste(requiredKeys[!checks], collapse = ", ")))
}
private$custom_index = string
},
rpkyle marked this conversation as resolved.
Show resolved Hide resolved

# ------------------------------------------------------------------------
# modify the templated variables by using the `interpolate_index` method.
# ------------------------------------------------------------------------
interpolate_index = function(template_index = private$template_index[[1]], ...) {
template = template_index
kwargs <- list(...)

for (name in names(kwargs)) {
key = paste0('\\{\\%', name, '\\%\\}')
template = sub(key, kwargs[[name]], template)
}

cat(template)
rpkyle marked this conversation as resolved.
Show resolved Hide resolved

requiredKeys <- c("app_entry", "config", "scripts")
rpkyle marked this conversation as resolved.
Show resolved Hide resolved

checks <- sapply(requiredKeys, function(x) grepl(x, names(kwargs)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this is creative -- I wouldn't have thought of doing it this way.

I wonder if this might be a little more succinct, though; you try something like

        stop(sprintf("Did you forget to include %s in your index string?", 
                     paste(setdiff(required_keys, names(kwargs)), collapse = ", ")))

For generating the vector of booleans (logicals), I'd suggest using vapply instead of sapply; I try to avoid the latter when writing package code since it is not type-safe.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I found a way to make this a little more concise in 5ae7549.


if (FALSE %in% checks) {
stop(sprintf("Did you forget to include %s in your index string?",
paste(requiredKeys[!checks], collapse = ", ")))
}

private$template_index = template
},

# ------------------------------------------------------------------------
# convenient fiery wrappers
# ------------------------------------------------------------------------
run_server = function(host = Sys.getenv('HOST', "127.0.0.1"),
port = Sys.getenv('PORT', 8050),

block = TRUE,
showcase = FALSE,
use_viewer = FALSE,
Expand Down Expand Up @@ -1266,6 +1356,24 @@ Dash <- R6::R6Class(

# akin to https://github.com/plotly/dash/blob/d2ebc837/dash/dash.py#L338
# note discussion here https://github.com/plotly/dash/blob/d2ebc837/dash/dash.py#L279-L284
custom_index = NULL,
template_index = c(
"<!DOCTYPE html>
<html>
<head>
{%meta_tags%}
<title>{%private$name%}</title>
{%favicon%}
{%css_tags%}
</head>
<body>
{%app_entry%}
<footer>
{%config%}
{%scripts%}
</footer>
</body>
</html>", NA),
.index = NULL,

generateReloadHash = function() {
Expand Down Expand Up @@ -1434,13 +1542,29 @@ Dash <- R6::R6Class(
css_tags <- all_tags[["css_tags"]]

# retrieve script tags for serving in the index
scripts_tags <- all_tags[["scripts_tags"]]
scripts <- all_tags[["scripts_tags"]]

# insert meta tags if present
meta_tags <- all_tags[["meta_tags"]]

# define the react-entry-point
app_entry <- "<div id='react-entry-point'><div class='_dash-loading'>Loading...</div></div>"
# define the dash default config key
config <- sprintf("<script id='_dash-config' type='application/json'> %s </script>", to_JSON(self$config))

if (!is.null(private$custom_index)) {
string_index <- glue::glue(private$custom_index, .open = "{%", .close = "%}")

private$.index <- string_index
}

else if (length(private$template_index) == 1) {
private$.index <- private$template_index
}

private$.index <- sprintf(
'<!DOCTYPE html>
else {
private$.index <- sprintf(
'<!DOCTYPE html>
<html>
<head>
%s
Expand All @@ -1450,23 +1574,22 @@ Dash <- R6::R6Class(
</head>

<body>
<div id="react-entry-point">
<div class="_dash-loading">Loading...</div>
</div>

%s
<footer>
<script id="_dash-config" type="application/json"> %s </script>
%s
%s
</footer>
</body>
</html>',
meta_tags,
private$name,
favicon,
css_tags,
to_JSON(self$config),
scripts_tags
)
meta_tags,
private$name,
favicon,
css_tags,
app_entry,
config,
scripts
)
}
}
)
)
Expand Down
17 changes: 17 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -1270,3 +1270,20 @@ tryCompress <- function(request, response) {
}
return(response$compress())
}

interpolate_str <- function(index_template, ...) {
# This function takes an index string, along with
# user specified keys for the html keys of the index
# and sets the default values of the keys to the
# ones specified by the keys themselves, returning
# the custom index template.
template = index_template
kwargs <- list(...)

for (name in names(kwargs)) {
key = paste0('\\{', name, '\\}')

template = sub(key, kwargs[[name]], template)
}
return(template)
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![CircleCI](https://circleci.com/gh/plotly/dashR/tree/dev.svg?style=svg)](https://circleci.com/gh/plotly/dashR/tree/dev)
[![CircleCI](https://circleci.com/gh/plotly/dashR/tree/master.svg?style=svg)](https://circleci.com/gh/plotly/dashR/tree/master)
[![GitHub](https://img.shields.io/github/license/plotly/dashR.svg?color=dark-green)](https://github.com/plotly/dashR/blob/master/LICENSE)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/plotly/dashR.svg?color=dark-green)](https://github.com/plotly/dashR/graphs/contributors)

Expand Down