Skip to content

Commit fdb7952

Browse files
committed
tolerate gtsummary in save_txt
1 parent 4e3add1 commit fdb7952

File tree

3 files changed

+100
-20
lines changed

3 files changed

+100
-20
lines changed

R/save_as_txt.R

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@ create_yaml_header_txt <- function(object_path, pkg_to_attach) {
1414
"knitr::opts_chunk$set(echo = FALSE, message = FALSE)",
1515
paste0("library(", pkg_to_attach, ")"),
1616
paste("x <-", rlang::call2("readRDS", file = as.character(object_path)) |> rlang::expr_deparse(width = Inf)),
17-
"if (!inherits(x, 'list') || inherits(x, 'gt_tbl')) x <- list(x)",
17+
"if (!inherits(x, 'list') || inherits(x, 'gt_tbl') || inherits(x, 'gtsummary')) x <- list(x)",
1818
"as_kable_txt <- function(obj) {",
19-
" knitr::kable(gt:::dt_data_get(obj))",
19+
" if (inherits(obj, 'gtsummary')) {",
20+
" gtsummary::as_kable(obj)",
21+
" } else {",
22+
" knitr::kable(gt:::dt_data_get(obj))",
23+
" }",
2024
"}",
2125
"```",
2226
""
@@ -69,11 +73,11 @@ save_txt_with_rmarkdown <- function(x,
6973
# convert path to absolute path to ensure output is created in the correct location
7074
path <- normalizePath(path, winslash = "/", mustWork = FALSE)
7175

72-
# txt output only supports gt_tbl objects
73-
check_class(x, cls = c("gt_tbl", "list"))
74-
if (is_simple_list(x) && some(x, ~ !inherits(.x, "gt_tbl"))) {
76+
# txt output supports gt_tbl and gtsummary objects
77+
check_class(x, cls = c("gt_tbl", "gtsummary", "list"))
78+
if (is_simple_list(x) && some(x, ~ !inherits(.x, c("gt_tbl", "gtsummary")))) {
7579
cli::cli_abort(
76-
"When argument {.arg x} is a list, each list element must be of class {.cls gt_tbl}.",
80+
"When argument {.arg x} is a list, each list element must be of class {.cls gt_tbl} or {.cls gtsummary}.",
7781
call = get_cli_abort_call()
7882
)
7983
}
@@ -84,8 +88,10 @@ save_txt_with_rmarkdown <- function(x,
8488
# save the input object to a tempfile (which will be loaded in the rmd file) -
8589
saveRDS(x, file = temp_file_x)
8690

87-
# gt package is always attached for gt_tbl objects
88-
pkg_to_attach <- "gt"
91+
# determine which packages need to be attached based on object class(es)
92+
x_list <- if (is_simple_list(x)) x else list(x)
93+
has_gtsummary <- some(x_list, ~ inherits(.x, "gtsummary"))
94+
pkg_to_attach <- if (has_gtsummary) c("gt", "gtsummary") else "gt"
8995

9096
# string of the yaml header and chunks
9197
chr_rmarkdown_yaml <- create_yaml_header_txt(temp_file_x, pkg_to_attach)
@@ -120,11 +126,14 @@ save_txt_with_rmarkdown <- function(x,
120126

121127
#' Save as txt (plain text / Markdown)
122128
#'
123-
#' Save a `gt_tbl` object as a plain text (Markdown-formatted) file via R markdown.
124-
#' Only `gt_tbl` objects (from the gt package) are supported for plain text output.
129+
#' Save a `gt_tbl` or `gtsummary` object as a plain text (Markdown-formatted)
130+
#' file via R markdown. Both `gt_tbl` objects (from the gt package) and
131+
#' `gtsummary` objects are supported for plain text output.
125132
#'
126-
#' @param x (`gt_tbl`/`list`)\cr
127-
#' object of class `'gt_tbl'` (gt table), or a list of `'gt_tbl'` objects.
133+
#' @param x (`gt_tbl`/`gtsummary`/`list`)\cr
134+
#' object of class `'gt_tbl'` (gt table) or `'gtsummary'`, or a list of
135+
#' such objects. Lists may contain a mix of `'gt_tbl'` and `'gtsummary'`
136+
#' objects.
128137
#' @param path (`path`)\cr
129138
#' path to save file to, e.g. `"rendered_table.txt"` or `"rendered_table.md"`.
130139
#' @param encoding (`string`)\cr

tests/testthat/_snaps/save_as_txt.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@
2828
save_txt(x = "not_a_table", path = tempfile(fileext = ".txt"))
2929
Condition
3030
Error in `save_txt()`:
31-
! The `x` argument must be class <gt_tbl/list>, not a string.
31+
! The `x` argument must be class <gt_tbl/gtsummary/list>, not a string.
3232

3333
---
3434

3535
Code
3636
save_txt(list("a", "b"), path = tempfile(fileext = ".txt"))
3737
Condition
3838
Error in `save_txt()`:
39-
! When argument `x` is a list, each list element must be of class <gt_tbl>.
39+
! When argument `x` is a list, each list element must be of class <gt_tbl> or <gtsummary>.
4040

41-
# save_txt() fails with non-gt_tbl objects
41+
# save_txt() fails with non-gt_tbl/gtsummary objects
4242

4343
Code
4444
save_txt(ft, path = tempfile(fileext = ".txt"))
4545
Condition
4646
Error in `save_txt()`:
47-
! The `x` argument must be class <gt_tbl/list>, not a <flextable> object.
47+
! The `x` argument must be class <gt_tbl/gtsummary/list>, not a <flextable> object.
4848

tests/testthat/test-save_as_txt.R

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
gt_tbl_single <- gt::gt(head(mtcars))
22
gt_tbl_list <- list(gt::gt(head(mtcars)), gt::gt(tail(mtcars)))
3+
gtsummary_single <- gtsummary::tbl_summary(gtsummary::trial[, c("age", "trt")])
4+
gtsummary_list <- list(
5+
gtsummary::tbl_summary(gtsummary::trial[, c("age", "trt")]),
6+
gtsummary::tbl_summary(gtsummary::trial[, c("grade", "trt")])
7+
)
38

49
test_that("save_txt() works with a single gt_tbl", {
510
file_path <- tempfile(fileext = ".txt")
@@ -15,7 +20,7 @@ test_that("save_txt() works with a single gt_tbl", {
1520
# setup chunk attaches gt
1621
expect_match(res[9], "library(gt)", fixed = TRUE)
1722
# first table chunk renders correctly
18-
expect_match(res[19], "as_kable_txt(x[[1]])", fixed = TRUE)
23+
expect_match(res[23], "as_kable_txt(x[[1]])", fixed = TRUE)
1924
})
2025

2126
test_that("save_txt() works with a list of gt_tbl objects", {
@@ -32,8 +37,8 @@ test_that("save_txt() works with a list of gt_tbl objects", {
3237
# setup chunk attaches gt
3338
expect_match(res[9], "library(gt)", fixed = TRUE)
3439
# both table chunks rendered
35-
expect_match(res[19], "as_kable_txt(x[[1]])", fixed = TRUE)
36-
expect_match(res[25], "as_kable_txt(x[[2]])", fixed = TRUE)
40+
expect_match(res[23], "as_kable_txt(x[[1]])", fixed = TRUE)
41+
expect_match(res[29], "as_kable_txt(x[[2]])", fixed = TRUE)
3742
})
3843

3944
test_that("save_txt() fails with incorrect inputs", {
@@ -63,7 +68,7 @@ test_that("save_txt() fails with incorrect inputs", {
6368
)
6469
})
6570

66-
test_that("save_txt() fails with non-gt_tbl objects", {
71+
test_that("save_txt() fails with non-gt_tbl/gtsummary objects", {
6772
# flextable is not supported for txt output
6873
skip_if_not_installed("flextable")
6974
ft <- flextable::flextable(head(mtcars))
@@ -82,3 +87,69 @@ test_that("save_txt() works with .md extension", {
8287
)
8388
expect_true(file.exists(file_path))
8489
})
90+
91+
test_that("save_txt() works with a single gtsummary object", {
92+
skip_if_not_installed("gtsummary")
93+
file_path <- tempfile(fileext = ".txt")
94+
expect_error(
95+
res <- gtsummary_single |>
96+
save_txt(path = file_path),
97+
NA
98+
)
99+
expect_true(file.exists(file_path))
100+
101+
# YAML header uses md_document output
102+
expect_match(res[3], "md_document", fixed = TRUE)
103+
# setup chunk attaches both gt and gtsummary
104+
expect_match(res[9], "library(gt)", fixed = TRUE)
105+
expect_match(res[10], "library(gtsummary)", fixed = TRUE)
106+
# as_kable_txt dispatches on gtsummary class
107+
expect_match(res, "gtsummary::as_kable(obj)", fixed = TRUE, all = FALSE)
108+
# first table chunk renders correctly
109+
expect_match(res[length(res) - 1L], "as_kable_txt(x[[1]])", fixed = TRUE)
110+
})
111+
112+
test_that("save_txt() works with a list of gtsummary objects", {
113+
skip_if_not_installed("gtsummary")
114+
file_path <- tempfile(fileext = ".txt")
115+
expect_error(
116+
res <- gtsummary_list |>
117+
save_txt(path = file_path),
118+
NA
119+
)
120+
expect_true(file.exists(file_path))
121+
122+
# both gt and gtsummary attached
123+
expect_match(res[9], "library(gt)", fixed = TRUE)
124+
expect_match(res[10], "library(gtsummary)", fixed = TRUE)
125+
# both table chunks rendered
126+
expect_match(res, "as_kable_txt(x[[1]])", fixed = TRUE, all = FALSE)
127+
expect_match(res, "as_kable_txt(x[[2]])", fixed = TRUE, all = FALSE)
128+
})
129+
130+
test_that("save_txt() works with a mixed list of gt_tbl and gtsummary objects", {
131+
skip_if_not_installed("gtsummary")
132+
mixed_list <- list(gt_tbl_single, gtsummary_single)
133+
file_path <- tempfile(fileext = ".txt")
134+
expect_error(
135+
res <- mixed_list |>
136+
save_txt(path = file_path),
137+
NA
138+
)
139+
expect_true(file.exists(file_path))
140+
141+
# both packages attached because list contains a gtsummary object
142+
expect_match(res[9], "library(gt)", fixed = TRUE)
143+
expect_match(res[10], "library(gtsummary)", fixed = TRUE)
144+
# both chunks present
145+
expect_match(res, "as_kable_txt(x[[1]])", fixed = TRUE, all = FALSE)
146+
expect_match(res, "as_kable_txt(x[[2]])", fixed = TRUE, all = FALSE)
147+
})
148+
149+
test_that("save_txt() only attaches gt when no gtsummary objects present", {
150+
res <- save_txt(gt_tbl_single, path = tempfile(fileext = ".txt"))
151+
152+
# only gt is attached — no gtsummary library() call in the header
153+
expect_match(res[9], "library(gt)", fixed = TRUE)
154+
expect_false(any(grepl("library(gtsummary)", res, fixed = TRUE)))
155+
})

0 commit comments

Comments
 (0)