Skip to content

Commit

Permalink
Allow extra sources of pronouns in f_eval()
Browse files Browse the repository at this point in the history
  • Loading branch information
lionel- committed Apr 8, 2016
1 parent 11a8069 commit 2198a89
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 4 deletions.
38 changes: 35 additions & 3 deletions R/f-eval.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,24 @@
#' \code{.env} and \code{.data}. These are thin wrappers around \code{.data}
#' and \code{.env} that throw errors if you try to access non-existent values.
#'
#' You can also provide additional sources of data by supplying named
#' lists or data frames in \code{...}. Each of these arguments will
#' get a pronoun based on the argument name. These names are
#' automatically prefixed with a dot. Contrarily to \code{data}, these
#' additional sources are not directly accessible. Therefore you have
#' to explicitly qualify them with the relevant pronoun to access
#' these data.
#'
#' @param f A one-sided formula. Any expressions wrapped in \code{ uq() } will
#' will be "unquoted", i.e. they will be evaluated, and the results inserted
#' back into the formula. See \code{\link{f_interp}} for more details.
#' @param data A list (or data frame). \code{find_data} is a generic used to
#' find the data associated with a given object. If you want to make
#' \code{f_eval} work for your own objects, you can define a method for this
#' generic.
#' @param ... Additional sources of pronouns. These should be named
#' lists or data frames. The names of the pronouns are automatically
#' prefixed with a dot.
#' @param x An object for which you want to find associated data.
#' @export
#' @examples
Expand All @@ -40,6 +51,13 @@
#' f_eval(~ .data$cyl, mtcars)
#' f_eval(~ .env$cyl, mtcars)
#'
#' # Additional pronouns can be added by supplying explicit:
#' f_eval(~ .iris$Species, iris = iris)
#'
#' # The data contained in these sources can only be accessed
#' # explicitly, through the pronoun. Direct access will fail:
#' \dontrun{f_eval(~ Species, iris = iris)}
#'
#' # Imagine you are computing the mean of a variable:
#' f_eval(~ mean(cyl), mtcars)
#' # How can you change the variable that's being computed?
Expand All @@ -56,15 +74,29 @@
#'
#' # Instead we need to use the prefix form of `$`.
#' f_eval(~ mean( `$`(.data, uq(var) )), mtcars)
f_eval <- function(f, data = NULL) {
f_eval <- function(f, data = NULL, ...) {
expr <- f_rhs(f_interp(f))

data <- find_data(data)
env <- environment(f)
explicit <- list(...)

if (length(explicit)) {
if (is.null(names(explicit))) {
stop("`explicit` should be named", call. = FALSE)
}
if (!all(vapply(explicit, is.list, logical(1)))) {
stop("`explicit` should contain lists or data frames", call. = FALSE)
}
}
names(explicit) <- vapply(names(explicit),
function(x) paste0(".", x), character(1))

expr_env <- new.env(parent = env)
expr_env$.env <- complain(env)
expr_env$.data <- complain(data)
sources <- c(.env = env, .data = list(data), explicit)
for (source in names(sources)) {
expr_env[[source]] <- complain(sources[[source]])
}

eval(expr, data, expr_env)
}
Expand Down
21 changes: 20 additions & 1 deletion man/f_eval.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions tests/testthat/test-f-eval.R
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,17 @@ test_that("f_eval does quasiquoting", {
x <- 10
expect_equal(f_eval(~ uq(quote(x))), 10)
})

test_that("look into explicit pronouns", {
expect_equal(f_eval(~ .mtcars$cyl, mtcars = mtcars), mtcars$cyl)
})

test_that("data of explicit pronouns cannot be directly accessed", {
expect_error(f_eval(~ cyl, mtcars = mtcars), "not found")
})

test_that("fails when explicit is malformed", {
expect_error(f_eval(~ ., iris, mtcars), "should be named")
expect_error(f_eval(~ ., mtcars = "string"), "should contain lists")
})

0 comments on commit 2198a89

Please sign in to comment.