Skip to contents

Retrieve code from teal_data object.

Usage

# S4 method for teal_data
get_code(object, deparse = TRUE, datanames = NULL, ...)

Arguments

object

(teal_data)

deparse

(logical) flag specifying whether to return code as character (deparse = TRUE) or as expression (deparse = FALSE).

datanames

[Experimental] (character) vector of dataset names to return the code for. For more details see the "Extracting dataset-specific code" section.

...

Parameters passed to internal methods. Currently, the only supported parameter is check_names (logical(1)) flag, which is TRUE by default. Function warns about missing objects, if they do not exist in code but are passed in datanames. To remove the warning, set check_names = FALSE.

Value

Either a character string or an expression. If datanames is used to request a specific dataset, only code that creates that dataset (not code that uses it) is returned. Otherwise, all contents of @code.

Details

Retrieve code stored in @code, which (in principle) can be used to recreate all objects found in @env. Use datanames to limit the code to one or more of the datasets enumerated in @datanames. If the code has not passed verification (with verify()), a warning will be prepended.

Extracting dataset-specific code

When datanames is specified, the code returned will be limited to the lines needed to create the requested datasets. The code stored in the @code slot is analyzed statically to determine which lines the datasets of interest depend upon. The analysis works well when objects are created with standard infix assignment operators (see ?assignOps) but it can fail in some situations.

Consider the following examples:

Case 1: Usual assignments.

data <- teal_data() |>
  within({
    foo <- function(x) {
      x + 1
    }
    x <- 0
    y <- foo(x)
  })
get_code(data, datanames = "y")

x has no dependencies, so get_code(data, datanames = "x") will return only the second call.
y depends on x and foo, so get_code(data, datanames = "y") will contain all three calls.

Case 2: Some objects are created by a function's side effects.

data <- teal_data() |>
  within({
    foo <- function() {
      x <<- x + 1
    }
    x <- 0
    foo()
    y <- x
  })
get_code(data, datanames = "y")

Here, y depends on x but x is modified by foo as a side effect (not by reassignment) and so get_code(data, datanames = "y") will not return the foo() call.
To overcome this limitation, code dependencies can be specified manually. Lines where side effects occur can be flagged by adding "# @linksto <object name>" at the end.
Note that within evaluates code passed to expr as is and comments are ignored. In order to include comments in code one must use the eval_code function instead.

data <- teal_data() |>
  eval_code("
    foo <- function() {
      x <<- x + 1
    }
    x <- 0
    foo() # @linksto x
    y <- x
  ")
get_code(data, datanames = "y")

Now the foo() call will be properly included in the code required to recreate y.

Note that two functions that create objects as side effects, assign and data, are handled automatically.

Here are known cases where manual tagging is necessary:

  • non-standard assignment operators, e.g. %<>%

  • objects used as conditions in if statements: if (<condition>)

  • objects used to iterate over in for loops: for(i in <sequence>)

  • creating and evaluating language objects, e.g. eval(<call>)

Examples

tdata1 <- teal_data()
tdata1 <- within(tdata1, {
  a <- 1
  b <- a^5
  c <- list(x = 2)
})
get_code(tdata1)
#> [1] "a <- 1\nb <- a^5\nc <- list(x = 2)"
get_code(tdata1, datanames = "a")
#> [1] "a <- 1"
get_code(tdata1, datanames = "b")
#> [1] "a <- 1\nb <- a^5"

tdata2 <- teal_data(x1 = iris, code = "x1 <- iris")
get_code(tdata2)
#> [1] "warning('Code was not verified for reproducibility.')\nx1 <- iris"
get_code(verify(tdata2))
#> [1] "x1 <- iris"