55# '
66# ' @param object (`qenv`)
77# ' @param deparse (`logical(1)`) flag specifying whether to return code as `character` or `expression`.
8+ # ' @param names `r lifecycle::badge("experimental")` (`character`) vector of object names to return the code for.
9+ # ' For more details see the "Extracting dataset-specific code" section.
810# ' @param ... see `Details`
911# '
12+ # '
13+ # ' @section Extracting dataset-specific code:
14+ # ' When `names` is specified, the code returned will be limited to the lines needed to _create_
15+ # ' the requested objects. The code stored in the `@code` slot is analyzed statically to determine
16+ # ' which lines the objects of interest depend upon. The analysis works well when objects are created
17+ # ' with standard infix assignment operators (see `?assignOps`) but it can fail in some situations.
18+ # '
19+ # ' Consider the following examples:
20+ # '
21+ # ' _Case 1: Usual assignments._
22+ # ' ```r
23+ # ' q1 <- qenv() |>
24+ # ' within({
25+ # ' foo <- function(x) {
26+ # ' x + 1
27+ # ' }
28+ # ' x <- 0
29+ # ' y <- foo(x)
30+ # ' })
31+ # ' get_code(q1, names = "y")
32+ # ' ```
33+ # ' `x` has no dependencies, so `get_code(data, names = "x")` will return only the second call.\cr
34+ # ' `y` depends on `x` and `foo`, so `get_code(data, names = "y")` will contain all three calls.
35+ # '
36+ # ' _Case 2: Some objects are created by a function's side effects._
37+ # ' ```r
38+ # ' q2 <- qenv() |>
39+ # ' within({
40+ # ' foo <- function() {
41+ # ' x <<- x + 1
42+ # ' }
43+ # ' x <- 0
44+ # ' foo()
45+ # ' y <- x
46+ # ' })
47+ # ' get_code(q2, names = "y")
48+ # ' ```
49+ # ' Here, `y` depends on `x` but `x` is modified by `foo` as a side effect (not by reassignment)
50+ # ' and so `get_code(data, names = "y")` will not return the `foo()` call.\cr
51+ # ' To overcome this limitation, code dependencies can be specified manually.
52+ # ' Lines where side effects occur can be flagged by adding "`# @linksto <object name>`" at the end.\cr
53+ # ' Note that `within` evaluates code passed to `expr` as is and comments are ignored.
54+ # ' In order to include comments in code one must use the `eval_code` function instead.
55+ # '
56+ # ' ```r
57+ # ' q3 <- qenv() |>
58+ # ' eval_code("
59+ # ' foo <- function() {
60+ # ' x <<- x + 1
61+ # ' }
62+ # ' x <- 0
63+ # ' foo() # @linksto x
64+ # ' y <- x
65+ # ' ")
66+ # ' get_code(q3, names = "y")
67+ # ' ```
68+ # ' Now the `foo()` call will be properly included in the code required to recreate `y`.
69+ # '
70+ # ' Note that two functions that create objects as side effects, `assign` and `data`, are handled automatically.
71+ # '
72+ # ' Here are known cases where manual tagging is necessary:
73+ # ' - non-standard assignment operators, _e.g._ `%<>%`
74+ # ' - objects used as conditions in `if` statements: `if (<condition>)`
75+ # ' - objects used to iterate over in `for` loops: `for(i in <sequence>)`
76+ # ' - creating and evaluating language objects, _e.g._ `eval(<call>)`
77+ # '
1078# ' @return
1179# ' `get_code` returns the traced code (from `@code` slot) in the form specified by `deparse`.
1280# '
1381# ' @examples
1482# ' # retrieve code
83+ # ' q <- within(qenv(), {a <- 1; b <- 2})
1584# ' get_code(q)
1685# ' get_code(q, deparse = FALSE)
86+ # ' get_code(q, names = "a")
87+ # '
88+ # ' q <- qenv()
89+ # ' q <- eval_code(q, code = c("a <- 1", "b <- 2"))
90+ # ' get_code(q, names = "a")
1791# '
1892# ' @name get_code
1993# ' @rdname qenv
2094# ' @aliases get_code,qenv-method
2195# ' @aliases get_code,qenv.error-method
2296# '
2397# ' @export
24- setGeneric ("get_code ", function(object, deparse = TRUE, ...) {
98+ setGeneric ("get_code ", function(object, deparse = TRUE, names = NULL, ...) {
2599 # this line forces evaluation of object before passing to the generic
26100 # needed for error handling to work properly
27101 grDevices :: pdf(nullfile())
@@ -31,16 +105,24 @@ setGeneric("get_code", function(object, deparse = TRUE, ...) {
31105 standardGeneric(" get_code" )
32106})
33107
34- setMethod ("get_code ", signature = "qenv", function(object, deparse = TRUE) {
108+ setMethod ("get_code ", signature = "qenv", function(object, deparse = TRUE, names = NULL, ... ) {
35109 checkmate :: assert_flag(deparse )
110+ checkmate :: assert_character(names , min.len = 1L , null.ok = TRUE )
111+
112+ code <- if (! is.null(names )) {
113+ get_code_dependency(object @ code , names , ... )
114+ } else {
115+ object @ code
116+ }
117+
36118 if (deparse ) {
37- if (length(object @ code ) == 0 ) {
38- object @ code
119+ if (length(code ) == 0 ) {
120+ code
39121 } else {
40- paste(object @ code , collapse = " \n " )
122+ paste(code , collapse = " \n " )
41123 }
42124 } else {
43- parse(text = paste(c(" {" , object @ code , " }" ), collapse = " \n " ), keep.source = TRUE )
125+ parse(text = paste(c(" {" , code , " }" ), collapse = " \n " ), keep.source = TRUE )
44126 }
45127})
46128
0 commit comments