Nothing
#' Find all objects and variables used in a design or design step
#'
#' @description This internal function analyzes a DeclareDesign object to find all variables,
#' functions, and objects that are saved within the design steps. It identifies objects
#' from quosures, handler environments, and other design components to provide a comprehensive
#' view of objects the design depends on.
#'
#' This function is primarily used internally for design analysis and debugging purposes.
#' It helps identify dependencies and understand what objects a design relies on.
#'
#' @param design A design object or design step created using DeclareDesign functions
#'
#' @return A data.frame with columns:
#' \describe{
#' \item{name}{The name of the object/variable}
#' \item{value_str}{String representation of the object's value or type}
#' \item{step}{The step number where the object was found}
#' \item{quosure}{The name of the quosure or "handler" where the object was found}
#' \item{env}{The environment object where the variable was found}
#' }
#'
#' @importFrom rlang is_quosure as_quosure get_env env_label
#'
#' @keywords internal
find_all_objects <- function(design) {
if (!any(c("design_step", "design") %in% class(design)))
stop("Please pass a design object or design step")
if ("design_step" %in% class(design))
design <- design + NULL
results <- list()
# Step by step
for (step_i in seq_along(design)) {
step <- design[[step_i]]
jobs <- list()
handler_jobs <- list()
handler_arg_jobs <- list()
# --- Extract quosures from dots
dots <- attr(step, "dots")
if (!is.null(dots)) {
if ((length(names(dots)[names(dots) == ""]) > 1) & (attributes(step)$step_type != "model"))
stop(paste("More than one unnamed quosure in step", step_i))
names(dots)[names(dots) == ""] <- "formula"
for (quosure_name in names(dots)) {
q <- dots[[quosure_name]]
if (is_quosure(q)) {
q <- as_quosure(q)
env <- get_env(q)
jobs[[length(jobs) + 1]] <- list(
name = quosure_name,
env = env,
step = step_i
)
}
}
}
# --- Add handler environment
handler <- attr(step, "handler")
if (!is.null(handler)) {
handler_env <- get_env(handler)
# skip known internal handlers or package-defined handlers
# skip estimator steps since these are wrapped and messy
if (
(attr(step, "step_type") != "estimator") &&
!(attr(handler, "tag") %in% c("fabricate", "potential_outcomes_handler", "assignment_handler")) &&
!isNamespace(handler_env) &&
!startsWith(environmentName(handler_env), "namespace:")
) {
handler_name <- if (!is.null(attr(handler, "tag"))) attr(handler, "tag") else "handler"
handler_jobs[[length(handler_jobs) + 1]] <-
list(
name = handler_name,
env = handler_env,
step = step_i
)
for(arg in ls(handler_env)) {
handler_arg_jobs[[length(handler_arg_jobs) + 1]] <-
list(
name = arg,
env = handler_env,
step = step_i
)
}
}
}
# --- Process all jobs (quosures + handler)
#########################################
for (job in jobs) {
for (name in ls(job$env, all.names = TRUE)) {
# hide currydata internals
if (name %in% c("dots", "quoData", "quoNoData")) next
val <- tryCatch(get(name, envir = job$env), error = function(e) "<error>")
val_str <- tryCatch({
if (is.atomic(val) && length(val) <= 5) {
paste0(deparse(val), collapse = "")
} else if (is.function(val)) {
"function"
} else {
paste0("<", class(val)[1], ">")
}
}, error = function(e) "<error>")
results[[length(results) + 1]] <- data.frame(
name = name,
value_str = val_str,
step = job$step,
quosure = job$name,
env = I(list(job$env)),
stringsAsFactors = FALSE
)
}
}
# handler arg jobs
#########################################
for (job in handler_arg_jobs) {
val <- tryCatch(get(job$name, envir = job$env), error = function(e) "<error>")
val_str <- tryCatch({
if (is.atomic(val) && length(val) <= 5) {
paste0(deparse(val), collapse = "")
} else if (is.function(val)) {
"function"
} else {
paste0("<", class(val)[1], ">")
}
}, error = function(e) "<error>")
results[[length(results) + 1]] <- data.frame(
name = job$name,
value_str = val_str,
step = job$step,
quosure = "handler",
env = I(list(job$env)),
stringsAsFactors = FALSE
)
}
# should be only one handler per step
for (job in handler_jobs) {
results[[length(results) + 1]] <- data.frame(
name = job$name,
value_str = "handler",
step = job$step,
quosure = job$name,
env = I(list(job$env)),
stringsAsFactors = FALSE
)
}
}
x <- do.call(rbind, results)
if(is.null(x))
x <- data.frame()
row.names(x) <- NULL
# env_label <- vapply(lapply(results, `[[`, "env"),
# function(z) rlang::env_label(z[[1]]), character(1))
# x$env_label <- env_label
class(x) <- c("objects", class(x))
x
}
#' @keywords internal
#' @exportS3Method print objects
print.objects <- function(x, ...) {
if(nrow(x) ==0) {
print("No parameters")
return(NULL)
}
tmp <- x[c("name", "value_str", "step")] |> unique()
# aggregate steps per (name, value_str)
out <- aggregate(
step ~ name + value_str,
data = tmp,
FUN = function(s) paste(sort(unique(s)), collapse = ", ")
)
# rename aggregated column
names(out)[names(out) == "step"] <- "steps"
# ensure plain data.frame (no tibble class if it ever sneaks in)
out <- as.data.frame(out, stringsAsFactors = FALSE, check.names = FALSE)
# sort by name (case insensitive)
out <- out[order(tolower(out$name), na.last = TRUE), , drop = FALSE]
# print and return invisibly
print(out, row.names = FALSE)
invisible(out)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.