Skip to content

Commit f0b7f7c

Browse files
committed
introduce label to eval_code, within and get_code
1 parent 0c92d78 commit f0b7f7c

File tree

7 files changed

+90
-33
lines changed

7 files changed

+90
-33
lines changed

R/qenv-eval_code.R

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#' @param code (`character`, `language` or `expression`) code to evaluate.
1010
#' It is possible to preserve original formatting of the `code` by providing a `character` or an
1111
#' `expression` being a result of `parse(keep.source = TRUE)`.
12+
#' @param label (`character` or `NULL`) when provided, a name of the `code` that can be used to pull
13+
#' specific code elements with `get_code()`.
1214
#'
1315
#' @return
1416
#' `qenv` environment with `code/expr` evaluated or `qenv.error` if evaluation fails.
@@ -21,15 +23,20 @@
2123
#' q <- eval_code(q, quote(library(checkmate)))
2224
#' q <- eval_code(q, expression(assert_number(a)))
2325
#'
24-
#' @aliases eval_code,qenv,character-method
25-
#' @aliases eval_code,qenv,language-method
26-
#' @aliases eval_code,qenv,expression-method
27-
#' @aliases eval_code,qenv.error,ANY-method
26+
#' @aliases eval_code,qenv,character,character-method
27+
#' @aliases eval_code,qenv,language,character-method
28+
#' @aliases eval_code,qenv,expression,character-method
29+
#' @aliases eval_code,qenv.error,ANY,character-method
2830
#'
2931
#' @export
30-
setGeneric("eval_code", function(object, code) standardGeneric("eval_code"))
32+
setGeneric("eval_code", function(object, code, label) {
33+
if (missing(label)) label <- ""
34+
stopifnot("Label needs to have length 1." = length(label) == 1)
35+
standardGeneric("eval_code")
36+
})
37+
38+
setMethod("eval_code", signature = c("qenv", "character", "character"), function(object, code, label = "") {
3139

32-
setMethod("eval_code", signature = c("qenv", "character"), function(object, code) {
3340
parsed_code <- parse(text = code, keep.source = TRUE)
3441
object@.xData <- rlang::env_clone(object@.xData, parent = parent.env(.GlobalEnv))
3542
if (length(parsed_code) == 0) {
@@ -82,27 +89,28 @@ setMethod("eval_code", signature = c("qenv", "character"), function(object, code
8289
return(x)
8390
}
8491
attr(current_code, "dependency") <- extract_dependency(current_call)
92+
attr(current_code, "label") <- label
8593
object@code <- c(object@code, stats::setNames(list(current_code), sample.int(.Machine$integer.max, size = 1)))
8694
}
8795

8896
lockEnvironment(object@.xData, bindings = TRUE)
8997
object
9098
})
9199

92-
setMethod("eval_code", signature = c("qenv", "language"), function(object, code) {
93-
eval_code(object, code = paste(vapply(lang2calls(code), deparse1, collapse = "\n", character(1L)), collapse = "\n"))
100+
setMethod("eval_code", signature = c("qenv", "language", "character"), function(object, code, label = "") {
101+
eval_code(object, code = paste(vapply(lang2calls(code), deparse1, collapse = "\n", character(1L)), collapse = "\n"), label)
94102
})
95103

96-
setMethod("eval_code", signature = c("qenv", "expression"), function(object, code) {
104+
setMethod("eval_code", signature = c("qenv", "expression", "character"), function(object, code, label = "") {
97105
srcref <- attr(code, "wholeSrcref")
98106
if (length(srcref)) {
99-
eval_code(object, code = paste(attr(code, "wholeSrcref"), collapse = "\n"))
107+
eval_code(object, code = paste(attr(code, "wholeSrcref"), collapse = "\n"), label = label)
100108
} else {
101-
Reduce(eval_code, init = object, x = code)
109+
Reduce(function(obj, expr) eval_code(obj, expr, label), x = code, init = object)
102110
}
103111
})
104112

105-
setMethod("eval_code", signature = c("qenv.error", "ANY"), function(object, code) {
113+
setMethod("eval_code", signature = c("qenv.error", "ANY", "character"), function(object, code, label = "") {
106114
object
107115
})
108116

R/qenv-get_code.R

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
#' @param deparse (`logical(1)`) flag specifying whether to return code as `character` or `expression`.
88
#' @param ... internal usage, please ignore.
99
#' @param names (`character`) `r lifecycle::badge("experimental")` vector of object names to return the code for.
10-
#' For more details see the "Extracting dataset-specific code" section.
10+
#' For more details see the "Extracting dataset-specific code" section. Ignored when `labels` are provided.
11+
#' @param labels (`character`) vector of `labels`, attributes of code, specyfing which code elements to extract.
12+
#' Superior to `names` argument.
1113
#'
1214
#' @section Extracting dataset-specific code:
1315
#'
@@ -96,26 +98,32 @@
9698
#' @aliases get_code,qenv.error-method
9799
#'
98100
#' @export
99-
setGeneric("get_code", function(object, deparse = TRUE, names = NULL, ...) {
101+
setGeneric("get_code", function(object, deparse = TRUE, names = NULL, labels = NULL, ...) {
100102
dev_suppress(object)
101103
standardGeneric("get_code")
102104
})
103105

104-
setMethod("get_code", signature = "qenv", function(object, deparse = TRUE, names = NULL, ...) {
106+
setMethod("get_code", signature = "qenv", function(object, deparse = TRUE, names = NULL, labels = NULL, ...) {
105107
checkmate::assert_flag(deparse)
106108
checkmate::assert_character(names, min.len = 1L, null.ok = TRUE)
109+
checkmate::assert_character(labels, min.len = 1L, null.ok = TRUE)
107110

108-
# Normalize in case special it is backticked
109-
if (!is.null(names)) {
110-
names <- gsub("^`(.*)`$", "\\1", names)
111-
}
112-
113-
code <- if (!is.null(names)) {
114-
get_code_dependency(object@code, names, ...)
111+
if (!is.null(labels)) {
112+
code <- object@code[get_code_attr(object, "label") %in% labels]
115113
} else {
116-
object@code
114+
# Normalize in case special it is backticked
115+
if (!is.null(names)) {
116+
names <- gsub("^`(.*)`$", "\\1", names)
117+
}
118+
119+
code <- if (!is.null(names)) {
120+
get_code_dependency(object@code, names, ...)
121+
} else {
122+
object@code
123+
}
117124
}
118125

126+
119127
if (deparse) {
120128
paste(unlist(code), collapse = "\n")
121129
} else {

R/qenv-within.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#'
1010
#' @param data (`qenv`)
1111
#' @param expr (`expression`) to evaluate. Must be inline code, see `Using language objects...`
12+
#' @param label (`character`) to be assigned to the `expr`, so it can be extracted using `get_code(labels)`.
1213
#' @param ... named argument value will substitute a symbol in the `expr` matched by the name.
1314
#' For practical usage see Examples section below.
1415
#'
@@ -47,7 +48,7 @@
4748
#'
4849
#' @export
4950
#'
50-
within.qenv <- function(data, expr, ...) {
51+
within.qenv <- function(data, expr, label = "", ...) {
5152
expr <- substitute(expr)
5253
extras <- list(...)
5354

@@ -61,7 +62,7 @@ within.qenv <- function(data, expr, ...) {
6162
# Inject extra values into expressions.
6263
calls <- lapply(calls, function(x) do.call(substitute, list(x, env = extras)))
6364

64-
eval_code(object = data, code = as.expression(calls))
65+
eval_code(object = data, code = as.expression(calls), label = label)
6566
}
6667

6768

man/eval_code.Rd

Lines changed: 8 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/get_code.Rd

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-qenv_eval_code.R

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,15 @@ testthat::test_that("Code executed with integer shorthand (1L) is the same as or
187187
q <- within(qenv(), a <- 1L)
188188
testthat::expect_identical(get_code(q), "a <- 1L")
189189
})
190+
191+
192+
# labels -------------------------------------------------------------------------------------------------------------
193+
194+
testthat::test_that("it is possible to pass label to eval_code", {
195+
testthat::expect_no_error(eval_code(qenv(), "a <- 1L", label = "code for a"))
196+
})
197+
198+
testthat::test_that("it is possible to pass label to eval_code if such label already exists", {
199+
q <- eval_code(qenv(), "a <- 1L", label = "code for a")
200+
testthat::expect_no_error(eval_code(q, "b <- 2L", label = "code for a"))
201+
})

tests/testthat/test-qenv_get_code.R

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,3 +958,26 @@ testthat::test_that("extracting code doesn't fail when lhs contains two or more
958958
q <- eval_code(qenv(), code)
959959
testthat::expect_silent(get_code(q, names = "l"))
960960
})
961+
962+
963+
# labels -------------------------------------------------------------------------------------------------------------
964+
965+
testthat::test_that("when labels are passed only code related to those labels is extracted", {
966+
q <- eval_code(qenv(), "a <- 1L", label = "code for a")
967+
q <- eval_code(q, "b <- 1L")
968+
q <- eval_code(q, "c <- 1L", label = "code for c")
969+
970+
testthat::expect_identical(get_code(q, labels = "code for a"), "a <- 1L")
971+
testthat::expect_identical(get_code(q, labels = "code for c"), "c <- 1L")
972+
})
973+
974+
testthat::test_that("names are ignored when labels are provided", {
975+
q <- eval_code(qenv(), "a <- 1L", label = "code for a")
976+
testthat::expect_identical(get_code(q, names = 'X', labels = "code for a"), "a <- 1L")
977+
})
978+
979+
testthat::test_that("it is possible to pass labels of length greater than 1", {
980+
q <- eval_code(qenv(), "a <- 1L", label = "code for a")
981+
q <- eval_code(q, "b <- 2L", label = "code for b")
982+
testthat::expect_identical(get_code(q, labels = c("code for a", "code for b")), c("a <- 1L\nb <- 2L"))
983+
})

0 commit comments

Comments
 (0)