Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds names() function and deprecates datanames() #347

Merged
merged 13 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ repos:
- methods
- rlang
- stats
- teal.code
- insightsengineering/teal.code
averissimo marked this conversation as resolved.
Show resolved Hide resolved
- utils
- yaml
- id: spell-check
Expand Down
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ Collate:
'join_keys.R'
'teal.data.R'
'teal_data-class.R'
'teal_data-datanames.R'
'teal_data-get_code.R'
'teal_data-names.R'
'teal_data-show.R'
'teal_data.R'
'testhat-helpers.R'
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ S3method("[[<-",join_keys)
S3method("join_keys<-",join_keys)
S3method("join_keys<-",teal_data)
S3method("names<-",join_keys)
S3method("names<-",teal_data)
S3method("parents<-",join_keys)
S3method("parents<-",teal_data)
S3method(c,join_key_set)
Expand All @@ -14,6 +15,8 @@ S3method(format,join_keys)
S3method(join_keys,default)
S3method(join_keys,join_keys)
S3method(join_keys,teal_data)
S3method(length,teal.data)
S3method(names,teal_data)
S3method(parents,join_keys)
S3method(parents,teal_data)
S3method(print,join_keys)
Expand Down
13 changes: 8 additions & 5 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

### Breaking changes

- soft deprecate `datanames` argument of `get_code()`. Use `names` instead.
- Soft deprecate `datanames` argument of `get_code()`. Use `names` instead.
- Soft deprecate of `datanames()`. Use `names()` instead.
- Deprecate of `datanames(x) <- value`. Does nothing, replace with renaming the objects inside the environment.


### Enhancements

- `datanames()`
- if `join_keys` are provided, the `datanames()` are now sorted in topological way (`Kahn` algorithm),
- `names()` function is introduced replacing `datanames`.
- if `join_keys` are provided, the `names()` are now sorted in topological way (`Kahn` algorithm),
which means the parent dataset always precedes the child dataset.
- are extended by the parent dataset name, if one of the child dataset exist in `datanames()` and
- are extended by the parent dataset name, if one of the child dataset exist in `names()` and
the connection between child-parent is set through `join_keys` and `parent` exist in `teal_data` environment.
- do not allow to set a dataset name that do not exist in `teal_data` environment.
- `teal_data` no longer set default `datanames()` based on `join_keys` names - it uses only data names.
- `teal_data` no longer set default `names()` based on `join_keys` names - it uses only data names.

### Miscellaneous

Expand Down
48 changes: 48 additions & 0 deletions R/deprecated.R
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,51 @@ get_join_keys <- function(...) {
get_labels <- function(...) {
.deprecate_function("get_labels()", "Use col_labels(data)")
}

#' Names of data sets in `teal_data` object
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' Use `names()` instead of `datanames()`.
#'
#' `datanames()` is deprecated. If object should be hidden, then use a `.` (dot)
#' prefix for the object's name.
#'
#' @param x (`teal_data` or `qenv_error`) object to access or modify
#' @param value (`character`) new value for `@datanames`; all elements must be names of variables existing in `@.xData`
#'
#' @return The contents of `@datanames` or `teal_data` object with updated `@datanames`.
#' @aliases `datanames<-.teal_data`
#'
#' @name datanames

#' @rdname datanames
#' @export
datanames <- function(x) {
lifecycle::deprecate_soft("0.6.1", "datanames()", details = "names()")
names(x)
}

#' @rdname datanames
#' @export
`datanames<-` <- function(x, value) {
lifecycle::deprecate_soft(
"0.6.1",
"`datanames<-`()",
details = "invalid to use `datanames()<-` or `names()<-` on an object of class `teal_data`. See ?names.teal_data"
)
names(x)
}

#' @rdname datanames
#' @export
#' @keywords internal
`names<-.teal_data` <- function(x, value) {
gogonzo marked this conversation as resolved.
Show resolved Hide resolved
lifecycle::deprecate_warn(
"0.6.1",
"`names<-.teal_data`()",
details = "invalid to use `datanames()<-` or `names()<-` on an object of class `teal_data`. See ?names.teal_data"
)
x
}
1 change: 0 additions & 1 deletion R/join_keys.R
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ join_keys.teal_data <- function(...) {
#' join_keys(td)
`join_keys<-.teal_data` <- function(x, value) {
join_keys(x@join_keys) <- value
datanames(x) <- x@datanames # datanames fun manages some exceptions
x
}

Expand Down
39 changes: 15 additions & 24 deletions R/teal_data-class.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,34 @@ setOldClass("join_keys")
#' @name teal_data-class
#' @rdname teal_data-class
#'
#' @slot env (`environment`) environment containing data sets and possibly auxiliary variables.
#' Access variables with [get_var()] or [`[[`].
#' No setter provided. Evaluate code to add variables into `@env`.
#' @slot code (`character`) vector representing code necessary to reproduce the contents of `@env`.
#' @slot .xData (`environment`) environment containing data sets and possibly
#' auxiliary variables.
#' Access variables with [get()], [`$`], [get_var()] or [`[[`].
#' No setter provided. Evaluate code to add variables into `@.xData`.
#' @slot code (`character`) vector representing code necessary to reproduce the
#' contents of `@.xData`.
#' Access with [get_code()].
#' No setter provided. Evaluate code to append code to the slot.
#' @slot id (`integer`) random identifier assigned to each element of `@code`. Used internally.
#' @slot id (`integer`) random identifier assigned to each element of `@code`.
#' Used internally.
#' @slot warnings (`character`) vector of warnings raised when evaluating code.
#' Access with [get_warnings()].
#' @slot messages (`character`) vector of messages raised when evaluating code.
#' @slot join_keys (`join_keys`) object specifying joining keys for data sets in `@env`.
#' @slot join_keys (`join_keys`) object specifying joining keys for data sets in
#' `@.xData`.
#' Access or modify with [join_keys()].
#' @slot datanames (`character`) vector of names of data sets in `@env`.
#' Used internally to distinguish them from auxiliary variables.
#' Access or modify with [datanames()].
#' @slot verified (`logical(1)`) flag signifying that code in `@code` has been proven to yield contents of `@env`.
#' @slot verified (`logical(1)`) flag signifying that code in `@code` has been
#' proven to yield contents of `@.xData`.
#' Used internally. See [`verify()`] for more details.
#'
#' @import teal.code
#' @keywords internal
setClass(
Class = "teal_data",
contains = "qenv",
slots = c(join_keys = "join_keys", datanames = "character", verified = "logical"),
slots = c(join_keys = "join_keys", verified = "logical"),
prototype = list(
join_keys = join_keys(),
datanames = character(0),
verified = logical(0)
)
)
Expand All @@ -53,18 +54,11 @@ setClass(
#' @param code (`character` or `language`) code to reproduce the `data`.
#' Accepts and stores comments also.
#' @param join_keys (`join_keys`) object
#' @param datanames (`character`) names of datasets passed to `data`.
#' Needed when non-dataset objects are needed in the `env` slot.
#' @rdname new_teal_data
#' @keywords internal
new_teal_data <- function(data,
code = character(0),
join_keys = join_keys(),
datanames = names(data)) {
new_teal_data <- function(data, code = character(0), join_keys = join_keys()) {
averissimo marked this conversation as resolved.
Show resolved Hide resolved
checkmate::assert_list(data)
checkmate::assert_class(join_keys, "join_keys")
if (is.null(datanames)) datanames <- character(0) # todo: allow to specify
checkmate::assert_character(datanames)
if (!any(is.language(code), is.character(code))) {
stop("`code` must be a character or language object.")
}
Expand All @@ -82,17 +76,14 @@ new_teal_data <- function(data,
new_env <- rlang::env_clone(list2env(data), parent = parent.env(.GlobalEnv))
lockEnvironment(new_env, bindings = TRUE)

datanames <- .get_sorted_datanames(datanames = datanames, join_keys = join_keys, env = new_env)

methods::new(
"teal_data",
env = new_env,
.xData = new_env,
code = code,
warnings = rep("", length(code)),
messages = rep("", length(code)),
id = id,
join_keys = join_keys,
datanames = datanames,
verified = verified
)
}
68 changes: 0 additions & 68 deletions R/teal_data-datanames.R

This file was deleted.

6 changes: 4 additions & 2 deletions R/teal_data-get_code.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
#'
#' Retrieve code from `teal_data` object.
#'
#' Retrieve code stored in `@code`, which (in principle) can be used to recreate all objects found in `@env`.
#' Use `names` to limit the code to one or more of the datasets enumerated in `@datanames`.
#' Retrieve code stored in `@code`, which (in principle) can be used to recreate
#' all objects found in `@.xData`.
#' Use `names` to limit the code to one or more of the datasets enumerated in
#' the environment.
#'
#' @section Extracting dataset-specific code:
#' When `names` is specified, the code returned will be limited to the lines needed to _create_
Expand Down
41 changes: 41 additions & 0 deletions R/teal_data-names.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#' Names of data sets in `teal_data` object
#'
#' Functions to get the names of a `teal_data` object.
#' The names are obtained from the objects listed in the `qenv` environment.
#'
#' Objects named with a `.` (dot) prefix will be ignored and not returned.
#' To get the names of all objects, use `ls(x, all.names = TRUE)`, however, it
#' will not group the names by the join_keys topological structure.
#'
#' @param x A (`teal_data`) object to access or modify.
#'
#' @return A character vector of names.
#'
#' @examples
#' td <- teal_data(iris = iris)
#' td <- within(td, mtcars <- mtcars)
#' names(td)
#'
#' td <- within(td, .CO2 <- CO2)
#' names(td) # '.CO2' will not be returned
#'
#' @export
names.teal_data <- function(x) {
# Sorting from `ls` can be safely done as environments don't have any order
# nor support numeric-index subsetting
envir <- as.environment(x)
.get_sorted_names(ls(envir = envir), join_keys(x), envir)
}

#' @export
length.teal.data <- function(x) length(ls(x))

#' @keywords internal
.get_sorted_names <- function(datanames, join_keys, env) {
child_parent <- sapply(datanames, parent, x = join_keys, USE.NAMES = TRUE, simplify = FALSE)

union(
intersect(unlist(topological_sort(child_parent)), ls(env, all.names = TRUE)),
datanames
)
}
2 changes: 1 addition & 1 deletion R/teal_data-show.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ setMethod("show", signature = "teal_data", function(object) {
} else {
cat("\u2716", "unverified teal_data object\n")
}
rlang::env_print(object@env)
rlang::env_print(teal.code::get_env(object))
})
16 changes: 9 additions & 7 deletions R/verify.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
#'
#' Checks whether code in `teal_data` object reproduces the stored objects.
#'
#' If objects created by code in the `@code` slot of `x` are `all_equal` to the contents of the `@env` slot,
#' If objects created by code in the `@code` slot of `x` are `all_equal` to the
#' contents of the `@.xData` slot,
#' the function updates the `@verified` slot to `TRUE` in the returned `teal_data` object.
#' Once verified, the slot will always be set to `TRUE`.
#' If the `@code` fails to recreate objects in `teal_data@env`, an error is raised.
#' If the `@code` fails to recreate objects in `teal_data`'s environment, an
#' error is raised.
#'
#' @return Input `teal_data` object or error.
#'
Expand Down Expand Up @@ -65,7 +67,7 @@ setMethod("verify", signature = "teal_data", definition = function(x) {
stop(conditionMessage(y), call. = FALSE)
}

reproduced <- isTRUE(all.equal(x@env, y@env))
reproduced <- isTRUE(all.equal(teal.code::get_env(x), teal.code::get_env(y)))
if (reproduced) {
x@verified <- TRUE
methods::validObject(x)
Expand All @@ -74,15 +76,15 @@ setMethod("verify", signature = "teal_data", definition = function(x) {
error <- "Code verification failed."

objects_diff <- vapply(
intersect(names(x@env), names(y@env)),
intersect(names(x), names(y)),
function(element) {
isTRUE(all.equal(x@env[[element]], y@env[[element]]))
isTRUE(all.equal(x[[element]], y[[element]]))
},
logical(1)
)

names_diff_other <- setdiff(names(y@env), names(x@env))
names_diff_inenv <- setdiff(names(x@env), names(y@env))
names_diff_other <- setdiff(names(y), names(x))
names_diff_inenv <- setdiff(names(x), names(y))

if (length(objects_diff)) {
error <- c(
Expand Down
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ reference:
- join_key
- join_keys
- join_keys<-
- names.teal_data
- names<-.join_keys
- parents
- parents<-
Expand Down
Loading
Loading