Skip to content

Commit 0983411

Browse files
authored
feat: connectors as S7 object (#136)
1 parent 17bc611 commit 0983411

27 files changed

+521
-311
lines changed

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: connector
22
Title: Streamlining Data Access in Clinical Research
3-
Version: 1.0.0.9000
3+
Version: 1.0.0.9001
44
Authors@R: c(
55
person("Cervan", "Girard", , "cgid@novonordisk.com", role = c("aut", "cre")),
66
person("Aksel", "Thomsen", , "oath@novonordisk.com", role = "aut"),
@@ -28,12 +28,12 @@ Imports:
2828
glue,
2929
haven,
3030
jsonlite,
31-
lifecycle,
3231
purrr,
3332
R6 (>= 2.4.0),
3433
readr,
3534
readxl,
3635
rlang,
36+
S7,
3737
utils,
3838
vroom,
3939
writexl,

NAMESPACE

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ S3method(log_write_connector,ConnectorDBI)
2525
S3method(log_write_connector,ConnectorFS)
2626
S3method(log_write_connector,default)
2727
S3method(print,ConnectorLogger)
28-
S3method(print,cnts_datasources)
29-
S3method(print,connectors)
30-
S3method(print,nested_connectors)
3128
S3method(read_cnt,ConnectorDBI)
3229
S3method(read_cnt,ConnectorFS)
3330
S3method(read_cnt,ConnectorLogger)
@@ -86,7 +83,6 @@ export(connector_dbi)
8683
export(connector_fs)
8784
export(connectors)
8885
export(create_directory_cnt)
89-
export(datasources)
9086
export(disconnect_cnt)
9187
export(download_cnt)
9288
export(download_directory_cnt)

NEWS.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# connector dev
2+
3+
## Breaking Changes
4+
* Previously deprecated `datasources()` function removed.
5+
6+
## Enhancements
7+
* Changed `connectors()` and `nested_connectors()` to be S7 classes for more robust creation and use.
8+
19
# connector 1.0.0
210

311
## Breaking Changes

R/connect.R

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ connect <- function(
104104
if (is.null(names(config))) {
105105
names(config) <- purrr::map(config, "name")
106106
cnts <- config |>
107-
purrr::map(\(x) connect(x, metadata, datasource, set_env))
107+
purrr::map(\(x) connect(x, metadata, datasource, set_env, logging))
108108

109109
return(do.call(nested_connectors, cnts))
110110
}
@@ -147,7 +147,7 @@ connect_from_config <- function(config) {
147147
config$datasources[[i]]$name <- config$datasources[[i]]$name[[1]]
148148
}
149149

150-
connections$datasources <- as_datasources(config["datasources"])
150+
connections$.datasources <- datasources(config[["datasources"]])
151151

152152
# Add metadata to the connections object
153153
if (!is.null(config$metadata)) {
@@ -157,15 +157,15 @@ connect_from_config <- function(config) {
157157
USE.NAMES = FALSE
158158
)
159159

160-
test <- any(names_co %in% ".md")
160+
test <- any(names_co %in% c(".metadata", ".datasources"))
161161

162162
if (test) {
163163
cli::cli_abort(
164-
"'.md' is a reserved name. It cannot be used as a name for a data source."
164+
"'.metadata' and '.datasources' are reserved names. They cannot be used as a name for a data source."
165165
)
166166
}
167167
# placeholder to be transformed as attribute in connectors
168-
connections$.md <- config[["metadata"]]
168+
connections$.metadata <- config[["metadata"]]
169169
}
170170

171171
do.call(what = connectors, args = connections)

R/connectors.R

Lines changed: 129 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
#' Holds a special list of individual connector objects for consistent use of
55
#' connections in your project.
66
#'
7-
#' @param ... Named individual [Connector] objects
7+
#' @param ... Named individual [Connector] objects.
8+
#' @param .metadata `list()` of named metadata to store in the `@metadata` property.
9+
#' @param .datasources `list()` of datasource specifications to store in the `@datasources` property.
10+
#' If `NULL` (default) will be derived based on `...` input.
811
#'
912
#' @examples
1013
#' # Create connectors objects
@@ -18,47 +21,146 @@
1821
#'
1922
#' cnts
2023
#'
21-
#' # Print the individual connector for more information
24+
#' # Print the individual Connector for more information
2225
#'
2326
#' cnts$sdtm
2427
#'
2528
#' cnts$adam
2629
#'
27-
#' @export
28-
connectors <- function(...) {
29-
x <- rlang::list2(...)
30-
ds_ <- x[["datasources"]]
30+
#' @name connectors
31+
NULL
32+
33+
#' @noRd
34+
construct_connectors <- function(
35+
...,
36+
.metadata = list(),
37+
.datasources = NULL
38+
) {
39+
if (is.null(.datasources)) {
40+
cnts <- substitute(rlang::list2(...))
41+
.datasources <- connectors_to_datasources(cnts)
42+
}
43+
44+
S7::new_object(
45+
.parent = list(...),
46+
metadata = .metadata,
47+
datasources = datasources(.datasources)
48+
)
49+
}
3150

32-
md_ <- if (is.null(x[[".md"]])) list() else x[[".md"]]
51+
#' @noRd
52+
validate_named <- function(x) {
53+
if (!rlang::is_named2(x)) {
54+
return("All elements must be named")
55+
}
56+
}
57+
58+
#' @noRd
59+
validate_datasources <- function(x) {
60+
if (any(rlang::have_name(x))) {
61+
return("All elements must be not be named")
62+
}
3363

34-
if (!is.null(ds_) && !inherits(ds_, "cnts_datasources")) {
35-
cli::cli_abort(
36-
"'datasources' is a reserved name. It cannot be used as a name for a data source."
64+
if (
65+
any(
66+
vapply(
67+
X = x,
68+
FUN = \(x) !setequal(c("name", "backend"), names(x)),
69+
FUN.VALUE = logical(1)
70+
)
3771
)
72+
) {
73+
return("Each datasource must have (only) 'name' and 'backend' specified")
3874
}
3975

40-
if (is.null(ds_)) {
41-
cnts <- substitute(rlang::list2(...))
42-
datasources <- connectors_to_datasources(cnts)
43-
} else {
44-
datasources <- ds_
76+
if (
77+
any(
78+
vapply(
79+
X = x,
80+
FUN = \(x) !"type" %in% names(x[["backend"]]),
81+
FUN.VALUE = logical(1)
82+
)
83+
)
84+
) {
85+
return("Each datasource must have backend type specified")
4586
}
87+
}
4688

47-
checkmate::assert_list(x = x, names = "named")
4889

49-
structure(
50-
x[!(names(x) %in% c("datasources", ".md"))],
51-
class = c("connectors"),
52-
datasources = datasources,
53-
metadata = md_
54-
)
90+
#' @noRd
91+
validate_connectors <- function(x) {
92+
if (!length(x)) {
93+
return("At least one Connector must be supplied")
94+
}
95+
96+
if (
97+
!all(
98+
vapply(
99+
X = x,
100+
FUN = \(x) is_connector(x),
101+
FUN.VALUE = logical(1)
102+
)
103+
)
104+
) {
105+
return("All elements must be a Connector object")
106+
}
107+
108+
if (length(x) != length(x@datasources)) {
109+
return("Each 'Connector' must have a corresponding datasource")
110+
}
111+
112+
validate_named(x)
55113
}
56114

115+
#' @noRd
116+
prop_metadata <- S7::new_property(
117+
class = S7::class_list,
118+
getter = \(self) self@metadata,
119+
validator = \(value) validate_named(value)
120+
)
121+
122+
#' @noRd
123+
datasources <- S7::new_class(
124+
name = "datasources",
125+
parent = S7::class_list,
126+
validator = \(self) validate_datasources(self)
127+
)
128+
129+
#' @noRd
130+
prop_datasources <- S7::new_property(
131+
class = datasources,
132+
getter = \(self) self@datasources
133+
)
134+
135+
#' @rdname connectors
57136
#' @export
58-
print.connectors <- function(x, ...) {
137+
connectors <- S7::new_class(
138+
name = "connectors",
139+
parent = S7::class_list,
140+
properties = list(
141+
metadata = prop_metadata,
142+
datasources = prop_datasources
143+
),
144+
constructor = construct_connectors,
145+
validator = \(self) validate_connectors(self)
146+
)
147+
148+
#' @noRd
149+
S7::method(print, connectors) <- function(x, ...) {
59150
print_connectors(x, ...)
60151
}
61152

153+
#' @noRd
154+
S7::method(print, datasources) <- function(x, ...) {
155+
print_datasources(x, ...)
156+
}
157+
158+
#' @noRd
159+
is_connectors <- function(x) {
160+
S7::S7_inherits(x, connectors) |
161+
S7::S7_inherits(x, nested_connectors)
162+
}
163+
62164
#' @noRd
63165
print_connectors <- function(x, ...) {
64166
classes <- x |>
@@ -95,11 +197,11 @@ print_connectors <- function(x, ...) {
95197
return(invisible(x))
96198
}
97199

98-
#' @export
99-
print.cnts_datasources <- function(x, ...) {
200+
#' @noRd
201+
print_datasources <- function(x, ...) {
100202
cli::cli_h1("Datasources")
101203

102-
for (ds in x[["datasources"]]) {
204+
for (ds in x) {
103205
cli::cli_h2(ds$name)
104206
cli::cli_ul()
105207
cli::cli_li("Backend Type: {.val {ds$backend$type}}")
@@ -110,39 +212,5 @@ print.cnts_datasources <- function(x, ...) {
110212
cli::cli_end()
111213
}
112214

113-
return(x)
114-
}
115-
116-
#' @noRd
117-
as_datasources <- function(...) {
118-
structure(
119-
...,
120-
class = "cnts_datasources"
121-
)
122-
}
123-
124-
#' Create a nested connectors object
125-
#'
126-
#' This function creates a nested connectors object from the provided arguments.
127-
#'
128-
#' @param ... Any number of connectors object.
129-
#'
130-
#' @return A list with class "nested_connectors" containing the provided arguments.
131-
#' @export
132-
nested_connectors <- function(...) {
133-
x <- rlang::list2(...)
134-
structure(
135-
x,
136-
class = c("nested_connectors")
137-
)
138-
}
139-
140-
#' @export
141-
print.nested_connectors <- function(x, ...) {
142-
print_connectors(x, ...)
143-
}
144-
145-
#' @noRd
146-
is_connectors <- function(connectors) {
147-
inherits(connectors, "connectors")
215+
return(invisible(x))
148216
}

0 commit comments

Comments
 (0)