@@ -52,6 +52,50 @@ markRenderFunction <- function(uiFunc, renderFunc, outputArgs = list()) {
52
52
hasExecuted = hasExecuted )
53
53
}
54
54
55
+ # ' Implement render functions
56
+ # '
57
+ # ' @param func A function without parameters, that returns user data. If the
58
+ # ' returned value is a promise, then the render function will proceed in async
59
+ # ' mode.
60
+ # ' @param transform A function that takes four arguments: \code{value},
61
+ # ' \code{session}, \code{name}, and \code{...} (for future-proofing). This
62
+ # ' function will be invoked each time a value is returned from \code{func},
63
+ # ' and is responsible for changing the value into a JSON-ready value to be
64
+ # ' JSON-encoded and sent to the browser.
65
+ # ' @param outputFunc The UI function that is used (or most commonly used) with
66
+ # ' this render function. This can be used in R Markdown documents to create
67
+ # ' complete output widgets out of just the render function.
68
+ # ' @param outputArgs A list of arguments to pass to the \code{outputFunc}.
69
+ # ' Render functions should include \code{outputArgs = list()} in their own
70
+ # ' parameter list, and pass through the value as this argument, to allow app
71
+ # ' authors to customize outputs. (Currently, this is only supported for
72
+ # ' dynamically generated UIs, such as those created by Shiny code snippets
73
+ # ' embedded in R Markdown documents).
74
+ # ' @return An annotated render function, ready to be assigned to an
75
+ # ' \code{output} slot.
76
+ # '
77
+ # ' @export
78
+ createRenderFunction <- function (
79
+ func , transform = function (value , session , name , ... ) value ,
80
+ outputFunc = NULL , outputArgs = NULL
81
+ ) {
82
+
83
+ renderFunc <- function (shinysession , name , ... ) {
84
+ res <- func()
85
+ if (inherits(res , " Promise" )) {
86
+ res %>> %
87
+ transform(shinysession , name , ... )
88
+ } else {
89
+ transform(res , shinysession , name , ... )
90
+ }
91
+ }
92
+
93
+ if (! is.null(outputFunc ))
94
+ markRenderFunction(outputFunc , renderFunc , outputArgs = outputArgs )
95
+ else
96
+ renderFunc
97
+ }
98
+
55
99
useRenderFunction <- function (renderFunc , inline = FALSE ) {
56
100
outputFunction <- attr(renderFunc , " outputFunc" )
57
101
outputArgs <- attr(renderFunc , " outputArgs" )
@@ -214,26 +258,25 @@ renderImage <- function(expr, env=parent.frame(), quoted=FALSE,
214
258
deleteFile = TRUE , outputArgs = list ()) {
215
259
installExprFunction(expr , " func" , env , quoted )
216
260
217
- renderFunc <- function (shinysession , name , ... ) {
218
- imageinfo <- func()
219
- # Should the file be deleted after being sent? If .deleteFile not set or if
220
- # TRUE, then delete; otherwise don't delete.
221
- if (deleteFile ) {
222
- on.exit(unlink(imageinfo $ src ))
223
- }
224
-
225
- # If contentType not specified, autodetect based on extension
226
- contentType <- imageinfo $ contentType %OR % getContentType(imageinfo $ src )
227
-
228
- # Extra values are everything in imageinfo except 'src' and 'contentType'
229
- extra_attr <- imageinfo [! names(imageinfo ) %in% c(' src' , ' contentType' )]
230
-
231
- # Return a list with src, and other img attributes
232
- c(src = shinysession $ fileUrl(name , file = imageinfo $ src , contentType = contentType ),
233
- extra_attr )
234
- }
235
-
236
- markRenderFunction(imageOutput , renderFunc , outputArgs = outputArgs )
261
+ createRenderFunction(func ,
262
+ transform = function (imageinfo , session , name , ... ) {
263
+ # Should the file be deleted after being sent? If .deleteFile not set or if
264
+ # TRUE, then delete; otherwise don't delete.
265
+ if (deleteFile ) {
266
+ on.exit(unlink(imageinfo $ src ))
267
+ }
268
+
269
+ # If contentType not specified, autodetect based on extension
270
+ contentType <- imageinfo $ contentType %OR % getContentType(imageinfo $ src )
271
+
272
+ # Extra values are everything in imageinfo except 'src' and 'contentType'
273
+ extra_attr <- imageinfo [! names(imageinfo ) %in% c(' src' , ' contentType' )]
274
+
275
+ # Return a list with src, and other img attributes
276
+ c(src = shinysession $ fileUrl(name , file = imageinfo $ src , contentType = contentType ),
277
+ extra_attr )
278
+ },
279
+ imageOutput , outputArgs )
237
280
}
238
281
239
282
@@ -273,15 +316,58 @@ renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
273
316
width = getOption(' width' ), outputArgs = list ()) {
274
317
installExprFunction(expr , " func" , env , quoted )
275
318
319
+ # TODO: Set a promise domain that sets the console width
320
+ # and captures output
321
+ # op <- options(width = width)
322
+ # on.exit(options(op), add = TRUE)
323
+
276
324
renderFunc <- function (shinysession , name , ... ) {
277
- op <- options(width = width )
278
- on.exit(options(op ), add = TRUE )
279
- paste(utils :: capture.output(func()), collapse = " \n " )
325
+ domain <- createRenderPrintPromiseDomain(width )
326
+ system2.5 :: withPromiseDomain(domain , {
327
+ p <- system2.5 :: Promise $ new()
328
+ p2 <- p $ then(function (value ) func())$ then(function (value ) {
329
+ res <- paste(readLines(domain $ conn , warn = FALSE ), collapse = " \n " )
330
+ res
331
+ })
332
+ p $ resolve(NULL )
333
+ p2 $ catch(function (err ) { cat(file = stderr(), " ERROR" , err $ message ) })
334
+ })
280
335
}
281
336
282
337
markRenderFunction(verbatimTextOutput , renderFunc , outputArgs = outputArgs )
283
338
}
284
339
340
+ createRenderPrintPromiseDomain <- function (width ) {
341
+ f <- file()
342
+
343
+ list (
344
+ conn = f ,
345
+ onThen = function (onFulfilled , onRejected ) {
346
+ res <- list (onFulfilled = onFulfilled , onRejected = onRejected )
347
+
348
+ if (is.function(onFulfilled )) {
349
+ res $ onFulfilled = function (result ) {
350
+ op <- options(width = width )
351
+ on.exit(options(op ), add = TRUE )
352
+
353
+ capture.output(onFulfilled(result ), file = f , append = TRUE , split = TRUE )
354
+ }
355
+ }
356
+
357
+ if (is.function(onRejected )) {
358
+ res $ onRejected = function (reason ) {
359
+ op <- options(width = width )
360
+ on.exit(options(op ), add = TRUE )
361
+
362
+ capture.output(onRejected(reason ), file = f , append = TRUE )
363
+ }
364
+ }
365
+
366
+ res
367
+ }
368
+ )
369
+ }
370
+
285
371
# ' Text Output
286
372
# '
287
373
# ' Makes a reactive version of the given function that also uses
@@ -313,12 +399,13 @@ renderText <- function(expr, env=parent.frame(), quoted=FALSE,
313
399
outputArgs = list ()) {
314
400
installExprFunction(expr , " func" , env , quoted )
315
401
316
- renderFunc <- function (shinysession , name , ... ) {
317
- value <- func()
318
- return (paste(utils :: capture.output(cat(value )), collapse = " \n " ))
319
- }
320
-
321
- markRenderFunction(textOutput , renderFunc , outputArgs = outputArgs )
402
+ createRenderFunction(
403
+ func ,
404
+ function (value , session , name , ... ) {
405
+ paste(utils :: capture.output(cat(value )), collapse = " \n " )
406
+ },
407
+ textOutput , outputArgs
408
+ )
322
409
}
323
410
324
411
# ' UI Output
0 commit comments