--- title: "qenv" author: "NEST coreDev" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{qenv} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ## Introduction to `qenv` A `qenv` is an R object which contains code and an environment and can be used to create reproducible outputs. ### Initialization The `qenv()` function serves as the gateway to create an initial `qenv` object: ```{r} library(teal.code) # create a new qenv object empty_qenv <- qenv() print(empty_qenv) ``` ### `qenv` basic usage The `eval_code()` function executes code within a `qenv` environment, yielding a new `qenv` object as the output. ```{r} # evaluate code in qenv my_qenv <- eval_code(empty_qenv, "x <- 2") print(my_qenv) get_env(my_qenv) q1 <- eval_code(my_qenv, "y <- x * 2") q1 <- eval_code(q1, "z <- y * 2") # my_qenv still contains only x print(my_qenv) ls(get_env(my_qenv)) # q1 contains x, y and z print(q1) ls(get_env(q1)) ``` The same result can be achieved with the `within` method for the `qenv` class. ```{r} q2 <- within(my_qenv, y <- x * 2) q2 <- within(q2, z <- y * 2) print(q2) ``` To extract objects from a `qenv`, use `[[`; this is particularly useful for displaying them in a `shiny` app. You can retrieve the code used to generate the `qenv` using the `get_code()` function. ```{r} print(q2[["y"]]) cat(get_code(q2)) ``` ### Substitutions In some cases, one may want to substitute some elements of the code before evaluation. Consider a case when a subset of `iris` is defined by an input value. ```{r} q <- qenv() q <- eval_code(q, quote(i <- subset(iris, Species == "setosa"))) q <- eval_code(q, substitute( ii <- subset(iris, Species == species), env = list(species = "versicolor") )) input_value <- "virginica" q <- eval_code(q, substitute( iii <- subset(iris, Species == species), env = list(species = input_value) )) summary(q[["i"]]$Species) summary(q[["ii"]]$Species) summary(q[["iii"]]$Species) ``` A more convenient way to pass code with substitution is to use the `within` method. ```{r} qq <- qenv() qq <- within(qq, i <- subset(iris, Species == "setosa")) qq <- within(qq, ii <- subset(iris, Species == species), species = "versicolor") input_value <- "virginica" qq <- within(qq, iii <- subset(iris, Species == species), species = input_value) summary(qq[["i"]]$Species) summary(qq[["ii"]]$Species) summary(qq[["iii"]]$Species) ``` See `?qenv` for more details. ### Combining `qenv` objects Given a pair of `qenv` objects, you may be able to "join" them, creating a new `qenv` object encompassing the union of both environments, along with the requisite code for reproduction: ```{r} common_q <- eval_code(qenv(), quote(x <- 1)) x_q <- eval_code(common_q, quote(y <- 5)) y_q <- eval_code(common_q, quote(z <- 5)) join_q <- join(x_q, y_q) print(join_q) ls(get_env(join_q)) ``` The feasibility of joining `qenv` objects hinges on the contents of the environments and the code's order. Refer to the function documentation for further details. ### Warnings and messages in `qenv` objects In cases where warnings or messages arise while evaluating code within a `qenv` environment, these are captured and stored within the `qenv` object. Access these messages and warnings using the `@` operator. ```{r} q_message <- eval_code(qenv(), quote(message("this is a message"))) q_message@messages q_warning <- eval_code(qenv(), quote(warning("and this is a warning"))) q_warning@warnings ``` If a particular line of code doesn't trigger any warnings or messages, the corresponding message/warning value will be an empty string. ```{r} q_message@warnings q_warning@messages ``` Additionally, a helper function, `get_warnings()`, is available to generate a formatted string comprising the warnings and the code responsible for generating them. It returns `NULL` when no warnings are present. ## Utilizing `qenv` inside `shiny` applications These functions can be seamlessly integrated into `shiny` applications to produce reproducible outputs. In the example below, the `rcode` section showcases the code employed for generating the output. When employing a `qenv` to evaluate code, should an error occur, an object of type `qenv.error` is generated. This object can be utilized wherever a `qenv` object is used, alleviating the need for code alterations to handle these errors. Select the `error_option` in the example below to witness `qenv` error handling in action. ```{r, eval=requireNamespace("shiny")} library(shiny) # create an initial qenv with the data in data_q <- qenv() data_q <- eval_code(data_q, "iris_data <- iris") ui <- fluidPage( radioButtons( "option", "Choose a column to plot:", c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "error_option") ), verbatimTextOutput("rcode"), plotOutput("plot") ) server <- function(input, output, session) { # create a qenv containing the reproducible output output_q <- reactive({ req(input$option) eval_code( data_q, bquote(p <- hist(iris_data[, .(input$option)])) ) }) # display output output$plot <- renderPlot(output_q()[["p"]]) # display code output$rcode <- renderText(get_code(output_q())) } if (interactive()) { shinyApp(ui, server) } ``` ## `qenv` and `teal` applications The versatile `qenv` object can seamlessly integrate into teal modules. Explore the teal vignette [Creating Custom Modules](https://insightsengineering.github.io/teal/latest-tag/articles/creating-custom-modules.html) for detailed guidance.