ambiorix-web / ambiorix

🖥️ Web framework for R
http://ambiorix.dev
GNU General Public License v3.0
211 stars 9 forks source link

refactor `convert_body()` and improve handling of `shiny` and `htmltools` tags #77

Closed kennedymwavu closed 1 day ago

kennedymwavu commented 2 days ago

refactoring

currently, convert_body() is defined as follows:

convert_body <- function(body) {
  if(inherits(body, "AsIs"))
    return(body)

  if(is.raw(body))
    return(body)

  if(is.character(body) && length(body) == 1L)
    return(body)

  if(is.factor(body) || inherits(body, "shiny.tag"))
    return(as.character(body))

  return(body)
}

only factor and shiny.tag objects are converted to character format. all other types are returned as-is. therefore, we can simplify this behavior by removing unnecessary conditions to reduce redundant checks:

convert_body <- function(body) {
  if (inherits(body, "AsIs")) {
    return(body)
  }
  if (is.factor(body) || inherits(body, "shiny.tag"))
    return(as.character(body))
  body
}

better handling for shiny & htmltools tags

after the refactoring, a better way to handle shiny & htmltools tags would be to use htmltools::doRenderTags(). this is so that we can handle both shiny.tag and shiny.tag.list classes without much hassle.

the reason is because currently trying to use res$send() on a tagList() throws an error. here's a reprex:

# app.R
library(ambiorix)
library(htmltools)

app <- Ambiorix$new()

app$get("/", \(req, res){
  x <- tagList(
    tags$h1("hello"),
    tags$h2("there")
  )
  res$send(x)
})

app$start()

and this is the error:

ℹ 12-11-2024 17:52:02 GET on /
Error in run_now(timeoutMs/1000, all = FALSE) : 
  Not compatible with requested type: [type=list; target=raw].
Calls: <Anonymous> -> invokeCppCallback

proposed new definition

putting all that into consideration, the new definition for convert_body() would thus be:

convert_body <- function(body) {
  if (inherits(body, "AsIs")) {
    return(body)
  }
  if (is.factor(body)) {
    return(as.character(body))
  }
  if (inherits(body, "shiny.tag") || inherits(body, "shiny.tag.list")) {
    return(htmltools::doRenderTags(body))
  }
  body
}