Bypassing IDE for displaying a Rmarkdown+Shiny document to users

rmarkdown
shiny
shinyappsio

#1

(Originally tagged with rmarkdown, but it was suggested to move the category.
The heart of the issue is something that the IDE does, as you'll see.)

I have an Rmarkdown doc with lots of embedded interactive shiny components and equations in TeX.
Works terrific! Convenient to edit the prose with Rmarkdown, and the shiny components can be interleaved freely.
(In fact, I have shiny-inside-Rmd-inside-shiny-inside-Rmd, & it works great!)

I wrote functions to ease re-use of the same type of component in many places,
each function producing both server and ui code,
all positioned nicely where they belong amongst the markdown text.
Ooo, as an author I love it.
See github.com:professorbeautiful/T15lumpsplit.
With Bias,variance,lump,split.Rmd,
just click the "Run Document" button,
then in the Preview Window click "Open in Browser".

BUT...
how can I make it easily available to a user/student?
(That is, hundreds of users.)
Goal: to do, in a function call or shell command,
the equivalent of the IDE's "Run Document" followed by "Open in Browser".
(You need "Open in Browser" because
the layout in viewer is so different that the viewer is useless.)

Being a fan of deployment to shinyapps.io, I tried wrapping the whole thing inside a shiny app wrapper. It "works", but (a) the Rmarkdown table-of-contents navigator doesn't appear and far more importantly (b) some shiny components (including all plots) in the Rmarkdown doc don't appear and all reactivity is broken.
That approach fails because the shiny output input and session objects are not in scope. Yet via some magic behind "Run Document", they are, and without the shiny wrapper the Rmarkdowndocument runs fine, all the shiny works great.

Surely if all the shiny code were moved into the wrapper shiny app,
the interactivity would work fine in the wrapper app...
but then the shiny components would be in the wrong places.

Trying to see how httpuv::startServer works,
but a little (or a lot) beyond me at this point.

If I could only locate the callback code behind the IDE's "Run Document" button, that would help a lot, and probably lead to a solution to the problem.
Been wandering around the RStudio repository.
but I can't find the answer.


#2

This is a bit of a sideways response to your question, but did you try deploying your interactive Rmd without another shiny layer, and according to the standard recommendation for interactive Rmds (a.k.a. "Shiny documents")? See here: https://bookdown.org/yihui/rmarkdown/shiny-deploy.html


#3

Thanks for the attempt.
I didn't think one can deploy a Rmd to ShinyApps.io.
But worth a try, since deploying apps to ShinyApps.io is comfortable for me.
(That would be perfect.)

And indeed it doesn't work, not with my file
nor with the standard "Untitled" New File/Rmarkdown/Shiny file.

The error is:

Error in function (type, msg, asError = TRUE) :
Calls: ... tryCatch -> tryCatchList -> tryCatchOne ->
and the entire log with trace is:
Deploy command:
rsconnect::deployApp(appDir = "~/Google Drive/T15lumpsplit/inst/T15lumpsplit", appFileManifest = "/var/folders/6v/y_cp6z4d22ggbgrqdk0g73v80000gv/T/ed06-40be-6bf4-bf83", appPrimaryDoc = "Bias-variance-smoothing-shrinking.Rmd", appSourceDoc = "~/Google Drive/T15lumpsplit/inst/T15lumpsplit/Bias-variance-smoothing-shrinking.Rmd", account = "trials", server = "shinyapps.io", appName = "Bias-variance-smoothing-shrinking", appTitle = "Bias-variance-smoothing-shrinking", launch.browser = function(url) { message("Deployment completed: ", url) }, lint = FALSE, metadata = list(asMultiple = FALSE, asStatic = FALSE), logLevel = "verbose")

Session information:
R version 3.5.0 (2018-04-23)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS High Sierra 10.13.5

Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats graphics grDevices utils datasets methods base

loaded via a namespace (and not attached):
[1] compiler_3.5.0 rsconnect_0.8.8
Cookies:
[1] "None"
----- Deployment error -----
Error in function (type, msg, asError = TRUE) :
Calls: ... tryCatch -> tryCatchList -> tryCatchOne ->

----- Error stack trace -----
19: stop(e)
18: value[3L]
17: tryCatchOne(expr, names, parentenv, handlers[[1L]])
16: tryCatchList(expr, classes, parentenv, handlers)
15: tryCatch({
if (!is.null(file)) {
RCurl::curlPerform(url = url, .opts = options, customrequest = method,
readfunction = fileContents, infilesize = fileLength,
writefunction = textGatherer$update, upload = TRUE)
}
else if (method == "DELETE") {
RCurl::curlPerform(url = url, .opts = options, customrequest = method)
}
else {
if (identical(method, "GET")) {
RCurl::getURL(url, .opts = options, write = textGatherer)
}
else {
RCurl::curlPerform(url = url, .opts = options, customrequest = method,
writefunction = textGatherer$update)
}
}
}, error = function(e, ...) {
if (identical(e$message, "Callback aborted") || identical(e$message,
"transfer closed with outstanding read data remaining"))
return(NULL)
else stop(e)
})
14: system.time(gcFirst = FALSE, tryCatch({
if (!is.null(file)) {
RCurl::curlPerform(url = url, .opts = options, customrequest = method,
readfunction = fileContents, infilesize = fileLength,
writefunction = textGatherer$update, upload = TRUE)
}
else if (method == "DELETE") {
RCurl::curlPerform(url = url, .opts = options, customrequest = method)
}
else {
if (identical(method, "GET")) {
RCurl::getURL(url, .opts = options, write = textGatherer)
}
else {
RCurl::curlPerform(url = url, .opts = options, customrequest = method,
writefunction = textGatherer$update)
}
}
}, error = function(e, ...) {
if (identical(e$message, "Callback aborted") || identical(e$message,
"transfer closed with outstanding read data remaining"))
return(NULL)
else stop(e)
}))
13: http(service$protocol, service$host, service$port, method, url,
headers, writer = writer, timeout = timeout, certificate = certificate)
12: httpRequest(service, authInfo, "GET", path, query, headers, writer,
timeout)
11: GET(service, authInfo, path, queryWithList)
10: grepl(contentType, response$contentType, fixed = TRUE)
9: isContentType(response, "application/json")
8: handleResponse(GET(service, authInfo, path, queryWithList))
7: listRequest(service, authInfo, path, query, "applications")
6: client$listApplications(accountInfo$accountId, filters = list(name = name))
5: getAppByName(client, accountInfo, target$appName)
4: applicationForTarget(client, accountDetails, target, forceUpdate)
3: force(code)
2: withStatus(paste0("Preparing to deploy ", assetTypeName), {
application <- applicationForTarget(client, accountDetails,
target, forceUpdate)
})
1: rsconnect::deployApp(appDir = "~/Google Drive/T15lumpsplit/inst/T15lumpsplit",
appFileManifest = "/var/folders/6v/y_cp6z4d22ggbgrqdk0g73v80000gv/T/ed06-40be-6bf4-bf83",
appPrimaryDoc = "Bias-variance-smoothing-shrinking.Rmd",
appSourceDoc = "~/Google Drive/T15lumpsplit/inst/T15lumpsplit/Bias-variance-smoothing-shrinking.Rmd",
account = "trials", server = "shinyapps.io", appName = "Bias-variance-smoothing-shrinking",
appTitle = "Bias-variance-smoothing-shrinking", launch.browser = function(url) {
message("Deployment completed: ", url)
}, lint = FALSE, metadata = list(asMultiple = FALSE, asStatic = FALSE),
logLevel = "verbose")


#4

It is absolutely possible to deploy RMarkdown documents using runtime: shiny to shinyapps.io

I'll try to take a look at your project on Github to see if I find something.


#5

Thank you, Josh! I'd encourage a look at my app (because it uses some novel authoring features), but you don't even have to do that, because the same deployment error crops up (for me) with the standard demo "Untitled" Rmd that you get from the menu pick
New File/Rmarkdown/Shiny file.
(I just tried to publish the demo file again from a different RStudio project, same error.)


#6

One thing of note, the shinyapps has been deprecated in favor of rsconnect


#7

I just created a new project and did just that and had no issues.


#8

Looks like I am using the rsconnect library.
Or do you mean that the ShinyApps.io site is deprecated??
Will I have to migrate my apps elsewhere?


#9

You Github repository includes shinyapps in the DESCRIPTION and in the Rmd you are using.
The rsconnect package should be used to deploy applications to shinyapps.io


#10

Ahh. Good catch. Doesn't explain the problem with the Untitled demo file, though.


#11

If you are having issues with a blank project and the generated RMarkdown Shiny document, then that should be a smaller use case to debug.

Are you behind a firewall of any kind?


#12

No firewall. At home.
I never used the Publish button before.
For my shiny apps, I have always used a couple of functions I wrote.
Let's see if there's a clue there.

.deploy
function(app=c("shinyElicit", "shinyCombinePlots"), reInstall=TRUE){
if(reInstall)
.installFromGithub("NNTbiomarkerHome")
apps = app
for (app in apps) {
if(substr(app, 1, 5) == "inst/")
warning(".deploy: do not include 'inst' in app name.")
cat("wd is " %&% getwd() %&% "\n")
cat("wd changing to " %&% "inst/" %&% app %&% "\n")
setwd("inst/" %&% app)
tryCatch({
require("shinyapps")
deployApp()
},
finally={
cat("shinyapps::showLogs(appDir = 'inst/" %&% app %&% "')\n")
setwd("../..")}
)
}
}

and

.installFromGithub
function(project)
devtools::install_github(paste0("professorbeautiful/",
project), build_vignettes=TRUE)


#13

rsconnect::accountInfo('trials', 'shinyapps.io')
does return my account info; looks correct.


#14

Aha, I tried bypassing the Publish button, with
rsconnect::deployDoc("Untitled.Rmd")
which after much led to...
Document successfully deployed to https://trials.shinyapps.io/untitled/

Then the web page loaded. But not Untitled. Instead got this error on the web page.
Error in enforcePackage(name, curVersion) :
The shiny package was not found in the library.
Calls: local ... eval -> eval -> eval -> -> enforcePackage
Execution halted


#15

Please update your functions to use rsconnect instead of shinyapps.


#16

What happens when you actually do use the Publish button?

What happens when you use rsconnect::deployApp() from the console?


#17

I just tried deployApp; the progress messages and the result is the same as with deployDoc, as described above. "Document successfully deployed" but the web page only has the error message;

**Error in enforcePackage(name, curVersion) : **
**  The shiny package was not found in the library.**
**Calls: local ... eval -> eval -> eval -> <Anonymous> -> enforcePackage**
**Execution halted**

The same result with Untitled demo or with the app I'm trying to deploy.

if I use the Publish button with my app

**inst/T15lumpsplit/Bias-variance-smoothing-shrinking.Rmd**
, it instantly returns with an error:
----- Deployment error -----
Error in function (type, msg, asError = TRUE)  : <not set>
Calls: <Anonymous> ... tryCatch -> tryCatchList -> tryCatchOne -> <Anonymous>
and a long stack trace. At the top is:
17: stop(e)
16: value[[3L]](cond)
15: tryCatchOne(expr, names, parentenv, handlers[[1L]])

if I use the Publish button with the Untitled demo,
Publish Document brings up a RStudio Connect Account window.
The shinyapps.io server address doesn't work in that window
(nor any variation I've tried-- www.shinyapps.io, trials.shinyapps.io , ...) .


#18

You cannot use the dialog for Connect Account to configure shinyapps.io:

Alternatively, you can use rsconnect in the console, per the documentation.

If you are unable to deploy a new project, with just the generated Rmd that has runtime: shiny, then let's just stick with figuring out the cause of that.


#19

With my application:

Then clicking "Republish":

Then clicking "Publish":


----- Deployment log started at  2018-07-12 13:39:08  -----
Deploy command: 
 rsconnect::deployApp(appDir = "~/Google Drive/T15lumpsplit/inst/T15lumpsplit",      appFileManifest = "/var/folders/6v/y_cp6z4d22ggbgrqdk0g73v80000gv/T/c126-a230-edd4-20d0",      appPrimaryDoc = "Bias-variance-smoothing-shrinking.Rmd",      appSourceDoc = "~/Google Drive/T15lumpsplit/inst/T15lumpsplit/Bias-variance-smoothing-shrinking.Rmd",      account = "trials", server = "shinyapps.io", appName = "bias-variance-smoothing-shrinking",      appId = 379146, launch.browser = function(url) {         message("Deployment completed: ", url)     }, lint = FALSE, metadata = list(asMultiple = FALSE, asStatic = FALSE),      logLevel = "verbose") 

Session information: 
R version 3.5.0 (2018-04-23)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS High Sierra 10.13.5

Matrix products: default
BLAS: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_3.5.0  rsconnect_0.8.8
Cookies: 
[1] "None"
----- Deployment error -----
Error in function (type, msg, asError = TRUE)  : <not set>
Calls: <Anonymous> ... tryCatch -> tryCatchList -> tryCatchOne -> <Anonymous>
 
----- Error stack trace -----
17: stop(e)
16: value[[3L]](cond)
15: tryCatchOne(expr, names, parentenv, handlers[[1L]])
14: tryCatchList(expr, classes, parentenv, handlers)
13: tryCatch({
        if (!is.null(file)) {
            RCurl::curlPerform(url = url, .opts = options, customrequest = method, 
                readfunction = fileContents, infilesize = fileLength, 
                writefunction = textGatherer$update, upload = TRUE)
        }
        else if (method == "DELETE") {
            RCurl::curlPerform(url = url, .opts = options, customrequest = method)
        }
        else {
            if (identical(method, "GET")) {
                RCurl::getURL(url, .opts = options, write = textGatherer)
            }
            else {
                RCurl::curlPerform(url = url, .opts = options, customrequest = method, 
                    writefunction = textGatherer$update)
            }
        }
    }, error = function(e, ...) {
        if (identical(e$message, "Callback aborted") || identical(e$message, 
            "transfer closed with outstanding read data remaining")) 
            return(NULL)
        else stop(e)
    })
12: system.time(gcFirst = FALSE, tryCatch({
        if (!is.null(file)) {
            RCurl::curlPerform(url = url, .opts = options, customrequest = method, 
                readfunction = fileContents, infilesize = fileLength, 
                writefunction = textGatherer$update, upload = TRUE)
        }
        else if (method == "DELETE") {
            RCurl::curlPerform(url = url, .opts = options, customrequest = method)
        }
        else {
            if (identical(method, "GET")) {
                RCurl::getURL(url, .opts = options, write = textGatherer)
            }
            else {
                RCurl::curlPerform(url = url, .opts = options, customrequest = method, 
                    writefunction = textGatherer$update)
            }
        }
    }, error = function(e, ...) {
        if (identical(e$message, "Callback aborted") || identical(e$message, 
            "transfer closed with outstanding read data remaining")) 
            return(NULL)
        else stop(e)
    }))
11: http(service$protocol, service$host, service$port, method, url, 
        headers, writer = writer, timeout = timeout, certificate = certificate)
10: httpRequest(service, authInfo, "GET", path, query, headers, writer, 
        timeout)
9: GET(service, authInfo, path)
8: grepl(contentType, response$contentType, fixed = TRUE)
7: isContentType(response, "application/json")
6: handleResponse(GET(service, authInfo, path))
5: client$getApplication(target$appId)
4: applicationForTarget(client, accountDetails, target, forceUpdate)
3: force(code)
2: withStatus(paste0("Preparing to deploy ", assetTypeName), {
       application <- applicationForTarget(client, accountDetails, 
           target, forceUpdate)
   })
1: rsconnect::deployApp(appDir = "~/Google Drive/T15lumpsplit/inst/T15lumpsplit", 
       appFileManifest = "/var/folders/6v/y_cp6z4d22ggbgrqdk0g73v80000gv/T/c126-a230-edd4-20d0", 
       appPrimaryDoc = "Bias-variance-smoothing-shrinking.Rmd", 
       appSourceDoc = "~/Google Drive/T15lumpsplit/inst/T15lumpsplit/Bias-variance-smoothing-shrinking.Rmd", 
       account = "trials", server = "shinyapps.io", appName = "bias-variance-smoothing-shrinking", 
       appId = 379146, launch.browser = function(url) {
           message("Deployment completed: ", url)
       }, lint = FALSE, metadata = list(asMultiple = FALSE, asStatic = FALSE), 
       logLevel = "verbose")
Error in function (type, msg, asError = TRUE)  : <not set>
Calls: <Anonymous> ... tryCatch -> tryCatchList -> tryCatchOne -> <Anonymous>
Timing stopped at: 0.008 0.002 0.042
Execution halted

#20

With only the right-out-of-the-box Rmarkdown->Shiny demo,
following the same steps, I don't even get the doc to appear with Run Document:

output file: /private/var/folders/6v/y_cp6z4d22ggbgrqdk0g73v80000gv/T/RtmpYQSY7t/Untitled-demo-Rmd-with-shiny.knit.md

Warning: Error in : $ operator is invalid for atomic vectors
  124: FUN
  123: lapply
  122: navbar_links_tags
  121: navbar_links_html
  120: navbar_html

NOTE: .GlobalEnv is empty.