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(), {
84+ # ' a <- 1
85+ # ' b <- 2
86+ # ' })
1587# ' get_code(q)
1688# ' get_code(q, deparse = FALSE)
89+ # ' get_code(q, names = "a")
90+ # '
91+ # ' q <- qenv()
92+ # ' q <- eval_code(q, code = c("a <- 1", "b <- 2"))
93+ # ' get_code(q, names = "a")
1794# '
1895# ' @name get_code
1996# ' @rdname qenv
2097# ' @aliases get_code,qenv-method
2198# ' @aliases get_code,qenv.error-method
2299# '
23100# ' @export
24- setGeneric ("get_code ", function(object, deparse = TRUE, ...) {
101+ setGeneric ("get_code ", function(object, deparse = TRUE, names = NULL, ...) {
25102 # this line forces evaluation of object before passing to the generic
26103 # needed for error handling to work properly
27104 grDevices :: pdf(nullfile())
@@ -31,16 +108,29 @@ setGeneric("get_code", function(object, deparse = TRUE, ...) {
31108 standardGeneric(" get_code" )
32109})
33110
34- setMethod ("get_code ", signature = "qenv", function(object, deparse = TRUE) {
111+ setMethod ("get_code ", signature = "qenv", function(object, deparse = TRUE, names = NULL, ... ) {
35112 checkmate :: assert_flag(deparse )
113+ checkmate :: assert_character(names , min.len = 1L , null.ok = TRUE )
114+
115+ # Normalize in case special it is backticked
116+ if (! is.null(names )) {
117+ names <- gsub(" ^`(.*)`$" , " \\ 1" , names )
118+ }
119+
120+ code <- if (! is.null(names )) {
121+ get_code_dependency(object @ code , names , ... )
122+ } else {
123+ object @ code
124+ }
125+
36126 if (deparse ) {
37- if (length(object @ code ) == 0 ) {
38- object @ code
127+ if (length(code ) == 0 ) {
128+ code
39129 } else {
40- paste(object @ code , collapse = " \n " )
130+ paste(code , collapse = " \n " )
41131 }
42132 } else {
43- parse(text = paste(c(" {" , object @ code , " }" ), collapse = " \n " ), keep.source = TRUE )
133+ parse(text = paste(c(" {" , code , " }" ), collapse = " \n " ), keep.source = TRUE )
44134 }
45135})
46136
0 commit comments