Skip to content

Commit 402969d

Browse files
author
Ryan Patrick Kyle
committed
Merge branch 'dev' of github.com:plotly/dashR into dev
2 parents 98254a1 + a2907ba commit 402969d

File tree

8 files changed

+426
-51
lines changed

8 files changed

+426
-51
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
# Change Log for Dash for R
22
All notable changes to this project will be documented in this file.
33

4+
## [0.3.0] - 2020-02-12
5+
### Added
6+
- Support for config-aware relative paths [#172](https://github.com/plotly/dashR/pull/172)
7+
- Support index customization and index templates [#168](https://github.com/plotly/dashR/pull/168)
8+
- Application titles may be set using the `app$title()` method, for parity with Dash for Python's `app.title` syntax [#168](https://github.com/plotly/dashR/pull/168)
9+
10+
### Changed
11+
- Dash for R now requires `dashCoreComponents` v1.8.0
12+
- Rename `DASH_HOST` to `HOST` and `DASH_PORT` to `PORT` [#167](https://github.com/plotly/dashR/pull/167)
13+
- Automatically set routes and requests pathname prefixes if `DASH_APP_NAME` environment variable has been set [#165](https://github.com/plotly/dashR/pull/165)
14+
15+
### Removed
16+
- Application titles can no longer be set using `name` parameter, which is now deprecated with a warning, for parity with Dash for Python [#168](https://github.com/plotly/dashR/pull/168)
17+
18+
419
## [0.2.0] - 2020-01-03
520
### Added
621
- Support for asynchronous/dynamic loading of dependencies, resource caching, and asset fingerprinting [#157](https://github.com/plotly/dashR/pull/157)

DESCRIPTION

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
Package: dash
22
Title: An Interface to the Dash Ecosystem for Authoring Reactive Web Applications
3-
Version: 0.2.0
4-
Authors@R: c(person("Chris", "Parmer", role = c("aut"), email = "[email protected]"), person("Ryan Patrick", "Kyle", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-5829-9867"), email = "[email protected]"), person("Carson", "Sievert", role = c("aut"), comment = c(ORCID = "0000-0002-4958-2844")), person(family = "Plotly Technologies", role = "cph"))
3+
Version: 0.3.0
4+
Authors@R: c(person("Chris", "Parmer", role = c("aut"), email = "[email protected]"), person("Ryan Patrick", "Kyle", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-5829-9867"), email = "[email protected]"), person("Carson", "Sievert", role = c("aut"), comment = c(ORCID = "0000-0002-4958-2844")), person("Hammad", "Khan", role = c("aut"), email = "[email protected]"), person(family = "Plotly Technologies", role = "cph"))
55
Description: A framework for building analytical web applications, Dash offers a pleasant and productive development experience. No JavaScript required.
66
Depends:
77
R (>= 3.0.2)
88
Imports:
99
dashHtmlComponents (== 1.0.2),
10-
dashCoreComponents (== 1.6.0),
11-
dashTable (== 4.5.1),
10+
dashCoreComponents (== 1.8.0),
11+
dashTable (== 4.6.0),
1212
R6,
1313
fiery (> 1.0.0),
1414
routr (> 0.2.0),
@@ -32,8 +32,8 @@ Collate:
3232
'print.R'
3333
'internal.R'
3434
Remotes: plotly/dash-html-components@55c3884,
35-
plotly/dash-core-components@c107e0f,
36-
plotly/dash-table@3058bd5
35+
plotly/dash-core-components@fc153b4,
36+
plotly/dash-table@79d46ca
3737
License: MIT + file LICENSE
3838
Encoding: UTF-8
3939
LazyData: true

NAMESPACE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ importFrom(routr,RouteStack)
2727
importFrom(routr,ressource_route)
2828
importFrom(stats,setNames)
2929
importFrom(tools,file_ext)
30-
importFrom(utils,getFromNamespace)
30+
importFrom(utils,getFromNamespace)

R/dash.R

Lines changed: 200 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#' @usage Dash
66
#'
77
#' @section Constructor: Dash$new(
8-
#' name = "dash",
8+
#' name = NULL,
99
#' server = fiery::Fire$new(),
1010
#' assets_folder = 'assets',
1111
#' assets_url_path = '/assets',
@@ -24,7 +24,7 @@
2424
#' @section Arguments:
2525
#' \tabular{lll}{
2626
#' `name` \tab \tab Character. The name of the Dash application (placed in the `<title>`
27-
#' of the HTML page).\cr
27+
#' of the HTML page). DEPRECATED; please use `index_string()` or `interpolate_index()` instead.\cr
2828
#' `server` \tab \tab The web server used to power the application.
2929
#' Must be a [fiery::Fire] object.\cr
3030
#' `assets_folder` \tab \tab Character. A path, relative to the current working directory,
@@ -100,6 +100,9 @@
100100
#' from the Dash backend. The latter may offer improved performance relative
101101
#' to callbacks written in R.
102102
#' }
103+
#' \item{`title("dash")`}{
104+
#' The title of the app. If no title is supplied, Dash for R will use 'dash'.
105+
#' }
103106
#' \item{`callback_context()`}{
104107
#' The `callback_context` method permits retrieving the inputs which triggered
105108
#' the firing of a given callback, and allows introspection of the input/state
@@ -114,12 +117,89 @@
114117
#' present a warning and return `NULL` if the Dash app was not loaded via `source()`
115118
#' if the `DASH_APP_PATH` environment variable is undefined.
116119
#' }
117-
#' \item{`run_server(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
118-
#' port = Sys.getenv('DASH_PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
120+
#' \item{`get_relative_path(path, requests_pathname_prefix)`}{
121+
#' The `get_relative_path` method simplifies the handling of URLs and pathnames for apps
122+
#' running locally and on a deployment server such as Dash Enterprise. It handles the prefix
123+
#' for requesting assets similar to the `get_asset_url` method, but can also be used for URL handling
124+
#' in components such as `dccLink` or `dccLocation`. For example, `app$get_relative_url("/page/")`
125+
#' would return `/app/page/` for an app running on a deployment server. The path must be prefixed with
126+
#' a `/`.
127+
#' \describe{
128+
#' \item{path}{Character. A path string prefixed with a leading `/` which directs at a path or asset directory.}
129+
#' \item{requests_pathname_prefix}{Character. The pathname prefix for the app on a deployed application. Defaults to the environment variable set by the server, or `""` if run locally.}
130+
#' }
131+
#' \item{`strip_relative_path(path, requests_pathname_prefix)`}{
132+
#' The `strip_relative_path` method simplifies the handling of URLs and pathnames for apps
133+
#' running locally and on a deployment server such as Dash Enterprise. It acts almost opposite the `get_relative_path`
134+
#' method, by taking a `relative path` as an input, and returning the `path` stripped of the `requests_pathname_prefiex`,
135+
#' and any leading or trailing `/`. For example, a path string `/app/homepage/`, would be returned as
136+
#' `homepage`. This is particularly useful for `dccLocation` URL routing.
137+
#' \describe{
138+
#' \item{path}{Character. A path string prefixed with a leading `/` and `requests_pathname_prefix` which directs at a path or asset directory.}
139+
#' \item{requests_pathname_prefix}{Character. The pathname prefix for the app on a deployed application. Defaults to the environment variable set by the server, or `""` if run locally.}
140+
#' }
141+
#' \item{`index_string(string)`}{
142+
#' The `index_string` method allows the specification of a custom index by changing
143+
#' the default `HTML` template that is generated by the Dash UI. Meta tags, CSS, Javascript,
144+
#' are some examples of features that can be modified.
145+
#' This method will present a warning if your HTML template is missing any necessary elements
146+
#' and return an error if a valid index is not defined. The following interpolation keys are
147+
#' currently supported:
148+
#' \describe{
149+
#' \item{`{%metas%}`}{Optional - The registered meta tags.}
150+
#' \item{`{%favicon%}`}{Optional - A favicon link tag if found in assets.}
151+
#' \item{`{%css%}`}{Optional - Link tags to css resources.}
152+
#' \item{`{%config%}`}{Required - Config generated by dash for the renderer.}
153+
#' \item{`{%app_entry%}`}{Required - The container where dash react components are rendered.}
154+
#' \item{`{%scripts%}`}{Required - Collected dependencies scripts tags.}
155+
#' }
156+
#' \describe{
157+
#' \item{Example of a basic HTML index string:}{
158+
#' \preformatted{
159+
#' "<!DOCTYPE html>
160+
#' <html>
161+
#' <head>
162+
#' \{\%meta_tags\%\}
163+
#' <title>\{\{%css\%\}\}</title>
164+
#' \{\%favicon\%\}
165+
#' \{\%css_tags\%\}
166+
#' </head>
167+
#' <body>
168+
#' \{\%app_entry\%\}
169+
#' <footer>
170+
#' \{\%config\%\}
171+
#' \{\%scripts\%\}
172+
#' </footer>
173+
#' </body>
174+
#' </html>"
175+
#' }
176+
#' }
177+
#' }
178+
#' }
179+
#' \item{`interpolate_index(template_index, ...)`}{
180+
#' With the `interpolate_index` method, we can pass a custom index with template string
181+
#' variables that are already evaluated. We can directly pass arguments to the `template_index`
182+
#' by assigning them to variables present in the template. This is similar to the `index_string` method
183+
#' but offers the ability to change the default components of the Dash index as seen in the example below:
184+
#' \preformatted{
185+
#' app$interpolate_index(
186+
#' template_index,
187+
#' metas = "<meta_charset='UTF-8'/>",
188+
#' renderer = renderer,
189+
#' config = config)
190+
#' }
191+
#' \describe{
192+
#' \item{template_index}{Character. A formatted string with the HTML index string. Defaults to the initial template}
193+
#' \item{...}{Named List. The unnamed arguments can be passed as individual named lists corresponding to the components
194+
#' of the Dash html index. These include the same arguments as those found in the `index_string()` template.}
195+
#' }
196+
#' }
197+
#' \item{`run_server(host = Sys.getenv('HOST', "127.0.0.1"),
198+
#' port = Sys.getenv('PORT', 8050), block = TRUE, showcase = FALSE, ...)`}{
119199
#' The `run_server` method has 13 formal arguments, several of which are optional:
120200
#' \describe{
121-
#' \item{host}{Character. A string specifying a valid IPv4 address for the Fiery server, or `0.0.0.0` to listen on all addresses. Default is `127.0.0.1` Environment variable: `DASH_HOST`.}
122-
#' \item{port}{Integer. Specifies the port number on which the server should listen (default is `8050`). Environment variable: `DASH_PORT`.}
201+
#' \item{host}{Character. A string specifying a valid IPv4 address for the Fiery server, or `0.0.0.0` to listen on all addresses. Default is `127.0.0.1` Environment variable: `HOST`.}
202+
#' \item{port}{Integer. Specifies the port number on which the server should listen (default is `8050`). Environment variable: `PORT`.}
123203
#' \item{block}{Logical. Start the server while blocking console input? Default is `TRUE`.}
124204
#' \item{showcase}{Logical. Load the Dash application into the default web browser when server starts? Default is `FALSE`.}
125205
#' \item{use_viewer}{Logical. Load the Dash application into RStudio's viewer pane? Requires that `host` is either `127.0.0.1` or `localhost`, and that Dash application is started within RStudio; if `use_viewer = TRUE` and these conditions are not satsified, the user is warned and the app opens in the default browser instead. Default is `FALSE`.}
@@ -174,7 +254,7 @@ Dash <- R6::R6Class(
174254
config = list(),
175255

176256
# i.e., the Dash$new() method
177-
initialize = function(name = "dash",
257+
initialize = function(name = NULL,
178258
server = fiery::Fire$new(),
179259
assets_folder = 'assets',
180260
assets_url_path = '/assets',
@@ -191,13 +271,18 @@ Dash <- R6::R6Class(
191271
suppress_callback_exceptions = FALSE) {
192272

193273
# argument type checking
194-
assertthat::assert_that(is.character(name))
195274
assertthat::assert_that(inherits(server, "Fire"))
196275
assertthat::assert_that(is.logical(serve_locally))
197276
assertthat::assert_that(is.logical(suppress_callback_exceptions))
198277

199278
# save relevant args as private fields
200-
private$name <- name
279+
if (!is.null(name)) {
280+
warning(sprintf(
281+
"The supplied application title, '%s', should be set using the title() method, or passed via index_string() or interpolate_index(); it has been ignored, and 'dash' will be used instead.",
282+
name),
283+
call. = FALSE
284+
)
285+
}
201286
private$serve_locally <- serve_locally
202287
private$eager_loading <- eager_loading
203288
# remove leading and trailing slash(es) if present
@@ -213,8 +298,8 @@ Dash <- R6::R6Class(
213298
private$in_viewer <- FALSE
214299

215300
# config options
216-
self$config$routes_pathname_prefix <- resolve_prefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX", url_base_pathname)
217-
self$config$requests_pathname_prefix <- resolve_prefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX", url_base_pathname)
301+
self$config$routes_pathname_prefix <- resolvePrefix(routes_pathname_prefix, "DASH_ROUTES_PATHNAME_PREFIX", url_base_pathname)
302+
self$config$requests_pathname_prefix <- resolvePrefix(requests_pathname_prefix, "DASH_REQUESTS_PATHNAME_PREFIX", url_base_pathname)
218303
self$config$external_scripts <- external_scripts
219304
self$config$external_stylesheets <- external_stylesheets
220305

@@ -763,11 +848,62 @@ Dash <- R6::R6Class(
763848
sep="/")))
764849
},
765850

851+
# ------------------------------------------------------------------------
852+
# return relative asset URLs
853+
# ------------------------------------------------------------------------
854+
855+
get_relative_path = function(path, requests_pathname_prefix = self$config$requests_pathname_prefix) {
856+
asset = get_relative_path(requests_pathname = requests_pathname_prefix, path = path)
857+
return(asset)
858+
},
859+
860+
861+
# ------------------------------------------------------------------------
862+
# return relative asset URLs
863+
# ------------------------------------------------------------------------
864+
865+
strip_relative_path = function(path, requests_pathname_prefix = self$config$requests_pathname_prefix) {
866+
asset = strip_relative_path(requests_pathname = requests_pathname_prefix, path = path)
867+
return(asset)
868+
},
869+
870+
# specify a custom index string
871+
# ------------------------------------------------------------------------
872+
index_string = function(string) {
873+
private$custom_index <- validate_keys(string)
874+
},
875+
876+
# ------------------------------------------------------------------------
877+
# modify the templated variables by using the `interpolate_index` method.
878+
# ------------------------------------------------------------------------
879+
interpolate_index = function(template_index = private$template_index[[1]], ...) {
880+
template = template_index
881+
kwargs <- list(...)
882+
883+
for (name in names(kwargs)) {
884+
key = paste0('\\{\\%', name, '\\%\\}')
885+
template = sub(key, kwargs[[name]], template)
886+
}
887+
888+
invisible(validate_keys(names(kwargs)))
889+
890+
private$template_index <- template
891+
},
892+
893+
# ------------------------------------------------------------------------
894+
# specify a custom title
895+
# ------------------------------------------------------------------------
896+
title = function(string = "dash") {
897+
assertthat::assert_that(is.character(string))
898+
private$name <- string
899+
},
900+
766901
# ------------------------------------------------------------------------
767902
# convenient fiery wrappers
768903
# ------------------------------------------------------------------------
769-
run_server = function(host = Sys.getenv('DASH_HOST', "127.0.0.1"),
770-
port = Sys.getenv('DASH_PORT', 8050),
904+
run_server = function(host = Sys.getenv('HOST', "127.0.0.1"),
905+
port = Sys.getenv('PORT', 8050),
906+
771907
block = TRUE,
772908
showcase = FALSE,
773909
use_viewer = FALSE,
@@ -848,7 +984,6 @@ Dash <- R6::R6Class(
848984
self$server$on('cycle-end', function(server, ...) {
849985
# handle case where assets are not present, since we can still hot reload the app itself
850986
#
851-
# private$last_refresh is set after the asset_map is refreshed
852987
# private$last_reload stores the time of the last hard or soft reload event
853988
# private$last_cycle will be set when the cycle-end handler terminates
854989
#
@@ -1266,6 +1401,24 @@ Dash <- R6::R6Class(
12661401

12671402
# akin to https://github.com/plotly/dash/blob/d2ebc837/dash/dash.py#L338
12681403
# note discussion here https://github.com/plotly/dash/blob/d2ebc837/dash/dash.py#L279-L284
1404+
custom_index = NULL,
1405+
template_index = c(
1406+
"<!DOCTYPE html>
1407+
<html>
1408+
<head>
1409+
{%meta_tags%}
1410+
<title>{%title%}</title>
1411+
{%favicon%}
1412+
{%css_tags%}
1413+
</head>
1414+
<body>
1415+
{%app_entry%}
1416+
<footer>
1417+
{%config%}
1418+
{%scripts%}
1419+
</footer>
1420+
</body>
1421+
</html>", NA),
12691422
.index = NULL,
12701423

12711424
generateReloadHash = function() {
@@ -1434,13 +1587,32 @@ Dash <- R6::R6Class(
14341587
css_tags <- all_tags[["css_tags"]]
14351588

14361589
# retrieve script tags for serving in the index
1437-
scripts_tags <- all_tags[["scripts_tags"]]
1590+
scripts <- all_tags[["scripts_tags"]]
14381591

14391592
# insert meta tags if present
14401593
meta_tags <- all_tags[["meta_tags"]]
1594+
1595+
# define the react-entry-point
1596+
app_entry <- "<div id='react-entry-point'><div class='_dash-loading'>Loading...</div></div>"
1597+
# define the dash default config key
1598+
config <- sprintf("<script id='_dash-config' type='application/json'> %s </script>", to_JSON(self$config))
1599+
1600+
if (is.null(private$name))
1601+
private$name <- 'dash'
1602+
1603+
if (!is.null(private$custom_index)) {
1604+
string_index <- glue::glue(private$custom_index, .open = "{%", .close = "%}")
1605+
1606+
private$.index <- string_index
1607+
}
1608+
1609+
else if (length(private$template_index) == 1) {
1610+
private$.index <- private$template_index
1611+
}
14411612

1442-
private$.index <- sprintf(
1443-
'<!DOCTYPE html>
1613+
else {
1614+
private$.index <- sprintf(
1615+
'<!DOCTYPE html>
14441616
<html>
14451617
<head>
14461618
%s
@@ -1450,23 +1622,22 @@ Dash <- R6::R6Class(
14501622
</head>
14511623
14521624
<body>
1453-
<div id="react-entry-point">
1454-
<div class="_dash-loading">Loading...</div>
1455-
</div>
1456-
1625+
%s
14571626
<footer>
1458-
<script id="_dash-config" type="application/json"> %s </script>
1627+
%s
14591628
%s
14601629
</footer>
14611630
</body>
14621631
</html>',
1463-
meta_tags,
1464-
private$name,
1465-
favicon,
1466-
css_tags,
1467-
to_JSON(self$config),
1468-
scripts_tags
1469-
)
1632+
meta_tags,
1633+
private$name,
1634+
favicon,
1635+
css_tags,
1636+
app_entry,
1637+
config,
1638+
scripts
1639+
)
1640+
}
14701641
}
14711642
)
14721643
)

0 commit comments

Comments
 (0)