Skip to content

Commit 0a90326

Browse files
gogonzoaverissimo
andauthored
Fix c.teal_card to append only new element (#345)
Ultimately closes #339 #340 It is not just to append content of `y` into `x`. `y` could be created from `x` and then add some NEW stuff. When merging back, it should not append stuff which is already in `x`. <details> <summary> Explaination with examples </summary> #### Joining completely new one ```r a <- teal_card("Text 1", "Text 2") b <- teal_card("Text 3") c(a, b) # should be: "Text 1", "Text 2", "Text 3" ``` #### Joining one which appends in the end ```r a <- teal_card("Text 1", "Text 2") b <- c(a, "Text 3") c(a, b) # should be: "Text 1", "Text 2", "Text 3" ``` #### Joining one which appends in the beginning ```r a <- teal_card("Text 1", "Text 2") b <- append(a, "Text 3", after = 0) c(a, b) # should be: "Text 3", "Text 1", "Text 2" ``` #### Joining one which modifies ```r a <- teal_card("Text 1", "Text 2") b <- a b[[1]] <- "Text 11" c(a, b) # should be: "Text 11", "Text 2" ``` #### Joining one which removes ```r a <- teal_card("Text 1", "Text 2") b <- a[-1] c(a, b) # should be: "Text 2" ``` Given all above, it it narrows down to: - replace entire `x` with `y` if they share some names (`y` is the one which is modified so it has a precedence) - otherwise just c(x, y) <details> --------- Signed-off-by: André Veríssimo <[email protected]> Co-authored-by: André Veríssimo <[email protected]>
1 parent 2b4f8de commit 0a90326

File tree

5 files changed

+111
-57
lines changed

5 files changed

+111
-57
lines changed

R/teal_card.R

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ teal_card <- function(x, ...) {
5151
x@teal_card
5252
} else {
5353
objects <- list(x, ...)
54+
names(objects) <- vapply(
55+
sample.int(.Machine$integer.max, size = length(objects)),
56+
function(x) substr(rlang::hash(list(Sys.time(), x)), 1, 8),
57+
character(1)
58+
)
5459
structure(objects, class = "teal_card")
5560
}
5661
}
@@ -91,10 +96,23 @@ c.teal_card <- function(...) {
9196
Reduce(
9297
f = function(u, v) {
9398
v <- as.teal_card(v)
94-
attrs <- utils::modifyList(attributes(u) %||% list(), attributes(v))
95-
result <- c(unclass(u), v)
96-
attributes(result) <- attrs
97-
result
99+
if (length(names(u)) && length(names(v)) && any(names(u) %in% names(v))) { # when v stems from u
100+
if (all(names(u) %in% names(v))) { # nothing from `u` is removed in `v`
101+
v
102+
} else {
103+
warning(
104+
"Appended `teal_card` doesn't remove some of the elements from previous `teal_card`.\n",
105+
"Restoring original content and adding only new items to the end of the card."
106+
)
107+
modifyList(u, v)
108+
}
109+
} else {
110+
attrs <- utils::modifyList(attributes(u) %||% list(), attributes(v))
111+
attrs$names <- union(names(u), names(v))
112+
result <- utils::modifyList(u, v)
113+
attributes(result) <- attrs
114+
result
115+
}
98116
},
99117
x = dots,
100118
init = list()

tests/testthat/test-Reporter.R

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ testthat::test_that("get_cards returns the same cards which was added to reporte
5959

6060
testthat::test_that("get_blocks returns the same blocks which was added to reporter, sep = NULL", {
6161
reporter <- test_reporter(card1 <- test_card1("A title"), card2 <- test_card2("Another title"))
62-
testthat::expect_identical(
62+
testthat::expect_equal(
6363
reporter$get_blocks(sep = NULL),
6464
append(
6565
c(sprintf("# %s", metadata(card1, "title")), card1),
6666
c(sprintf("# %s", metadata(card2, "title")), card2)
67-
)
67+
),
68+
ignore_attr = TRUE
6869
)
6970
})
7071

@@ -79,7 +80,7 @@ testthat::test_that("get_blocks by default adds NewpageBlock$new() between cards
7980
reporter_blocks <- reporter$get_blocks()
8081
reporter_blocks2 <- append(reporter_1$get_blocks(), "\\newpage")
8182
reporter_blocks2 <- append(reporter_blocks2, reporter_2$get_blocks())
82-
testthat::expect_equal(reporter$get_blocks(), reporter_blocks2)
83+
testthat::expect_equal(reporter$get_blocks(), reporter_blocks2, ignore_attr = TRUE)
8384
})
8485

8586
testthat::test_that("get_blocks and get_cards return empty list by default", {
@@ -99,7 +100,7 @@ testthat::test_that("The deep copy constructor copies the content files to new f
99100
testthat::expect_failure(
100101
testthat::expect_equal(rlang::obj_address(original_content_file), rlang::obj_address(copied_content_file))
101102
)
102-
testthat::expect_identical(original_content_file, copied_content_file)
103+
testthat::expect_equal(original_content_file, copied_content_file, ignore_attr = TRUE)
103104
})
104105

105106
testthat::describe("metadata", {
@@ -188,6 +189,7 @@ testthat::describe("to_list", {
188189

189190
testthat::describe("from_reporter", {
190191
it("returns same object from the same reporter", {
192+
shiny::reactiveConsole(TRUE)
191193
reporter <- test_reporter(card1 <- test_card1(), card2 <- test_card2())
192194
testthat::expect_identical(reporter, (Reporter$new()$from_reporter(reporter)))
193195
})
@@ -206,9 +208,10 @@ testthat::describe("from_reporter", {
206208
it("from_reporter persists the cards structure", {
207209
reporter1 <- test_reporter(test_card1(), test_card2())
208210
reporter2 <- teal.reporter::Reporter$new()
209-
testthat::expect_identical(
210-
unname(reporter1$get_cards()),
211-
unname(reporter2$from_reporter(reporter1)$get_cards())
211+
testthat::expect_equal(
212+
reporter1$get_cards(),
213+
reporter2$from_reporter(reporter1)$get_cards(),
214+
ignore_attr = TRUE
212215
)
213216
})
214217
})

tests/testthat/test-teal_card.R

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,23 @@ testthat::describe("teal_card contructor creates", {
44
testthat::expect_identical(doc, structure(list(), class = "teal_card"))
55
})
66

7+
testthat::it("teal_card appends arguments and sets them random unique names", {
8+
doc <- teal_card("a", "b", "c", "d", "e", "f", "g", "h")
9+
testthat::expect_true(all(!duplicated(names(doc))))
10+
})
11+
712
testthat::it("teal_card doesn't ignore NULL", {
8-
doc <- teal_card(NULL)
13+
doc <- unname(teal_card(NULL))
914
testthat::expect_identical(doc, structure(list(NULL), class = "teal_card"))
1015
})
1116

1217
testthat::it("teal_card keeps conditions", {
13-
doc <- teal_card(simpleCondition("test"))
18+
doc <- unname(teal_card(simpleCondition("test")))
1419
testthat::expect_identical(doc, structure(list(simpleCondition("test")), class = "teal_card"))
1520
})
1621

1722
testthat::it("teal_card appends each element asis (no list unwrapping)", {
18-
doc <- teal_card("a", list(1, list(2)), code_chunk("print('hi')"))
23+
doc <- unname(teal_card("a", list(1, list(2)), code_chunk("print('hi')")))
1924
testthat::expect_identical(
2025
doc,
2126
structure(
@@ -32,64 +37,87 @@ testthat::describe("c.teal_card combines", {
3237
})
3338

3439
it("empty teal_card with non-empty", {
35-
testthat::expect_identical(c(teal_card(), teal_card(TRUE)), teal_card(TRUE))
40+
doc2 <- teal_card(TRUE)
41+
testthat::expect_identical(c(teal_card(), doc2), doc2)
3642
})
3743

38-
it("with empty teal_card and remains the same", {
39-
testthat::expect_identical(c(teal_card("a", "b"), teal_card()), teal_card("a", "b"))
44+
it("with empty teal_card - remains the same", {
45+
doc <- teal_card("a", "b")
46+
testthat::expect_identical(c(doc, teal_card()), doc)
4047
})
4148

42-
it("with character, preserves class and append as a new element", {
49+
it("with character - adds as a new element", {
4350
doc_result <- c(teal_card("a", "b"), "c")
44-
testthat::expect_identical(doc_result, teal_card("a", "b", "c"))
51+
testthat::expect_equal(doc_result, teal_card("a", "b", "c"), ignore_attr = TRUE)
4552
})
4653

47-
it("with list, preserves the class and adds each element separately (unwraps list)", {
54+
it("with list - adds each list element separately (unwraps list)", {
4855
doc_result <- c(teal_card("a", "b"), list(1, 2))
49-
testthat::expect_identical(doc_result, teal_card("a", "b", 1, 2))
56+
testthat::expect_equal(doc_result, teal_card("a", "b", 1, 2), ignore_attr = TRUE)
5057
})
5158

52-
it("with teal_card containing a list and doesn't unwrap the list (asis)", {
59+
it("with teal_card containing a list - append this list asis (doesn't unwrap list)", {
5360
doc_result <- c(teal_card("a", "b"), teal_card(list(1, 2)))
54-
testthat::expect_identical(doc_result, teal_card("a", "b", list(1, 2)))
61+
testthat::expect_equal(doc_result, teal_card("a", "b", list(1, 2)), ignore_attr = TRUE)
5562
})
5663

57-
it("with NULL and remains the same (ignores NULL)", {
64+
it("with NULL - remains the same (ignores NULL)", {
5865
doc_result <- c(teal_card("a", "b"), NULL)
59-
testthat::expect_identical(doc_result, teal_card("a", "b"))
66+
testthat::expect_equal(doc_result, teal_card("a", "b"), ignore_attr = TRUE)
6067
})
6168

62-
it("with character(0) and appends as a new element", {
69+
it("with character(0) - adds as a new element", {
6370
doc_result <- c(teal_card("a", "b"), character(0))
64-
testthat::expect_identical(doc_result, teal_card("a", "b", character(0)))
65-
})
66-
67-
it("with teal_card and appends new elements asis", {
68-
doc_result <- c(teal_card("a", "b"), teal_card("c", "d"))
69-
testthat::expect_identical(doc_result, teal_card("a", "b", "c", "d"))
71+
testthat::expect_equal(doc_result, teal_card("a", "b", character(0)), ignore_attr = TRUE)
7072
})
7173

72-
it("with ggplot, preserves the class class and append as a new element", {
74+
it("with ggplot - adds as a new element", {
7375
plot <- ggplot2::ggplot(iris)
7476
doc_result <- c(teal_card("a", "b"), plot)
75-
testthat::expect_identical(doc_result, teal_card("a", "b", plot))
77+
testthat::expect_equal(doc_result, teal_card("a", "b", plot), ignore_attr = TRUE)
7678
})
7779

78-
it("with teal_card containing ggplot and appends elements asis", {
80+
it("with new teal_card - adds new elements asis", {
81+
doc_result <- c(teal_card("a", "b"), teal_card("c", "d"))
82+
testthat::expect_equal(doc_result, teal_card("a", "b", "c", "d"), ignore_attr = TRUE)
83+
})
84+
85+
it("with new teal_card containing ggplot - adds new elements asis", {
7986
plot <- ggplot2::ggplot(iris) +
8087
ggplot2::geom_point(ggplot2::aes(x = Sepal.Length, y = Sepal.Width))
8188
doc_result <- c(teal_card("a", "b"), teal_card("# Plot", plot))
82-
testthat::expect_identical(doc_result, teal_card("a", "b", "# Plot", plot))
89+
testthat::expect_equal(doc_result, teal_card("a", "b", "# Plot", plot), ignore_attr = TRUE)
90+
})
91+
92+
it("with teal_card containing new and old items - adds only new", {
93+
doc1 <- teal_card("a", "b")
94+
doc2 <- c(doc1, "c", "d")
95+
testthat::expect_equal(c(doc1, doc2), teal_card("a", "b", "c", "d"), ignore_attr = TRUE)
96+
})
97+
98+
it("with teal_card containing new and old items - adds even if their order is different", {
99+
doc1 <- teal_card("a", "b")
100+
doc2 <- c(doc1, "c", "d")
101+
doc2 <- doc2[c(3, 1, 4, 2)]
102+
testthat::expect_equal(c(doc1, doc2), teal_card("c", "a", "d", "b"), ignore_attr = TRUE)
103+
})
104+
105+
it("with teal_card with new and missing old items - restores original items, adds new at the end and warn", {
106+
doc1 <- teal_card("a", "b")
107+
doc2 <- c(doc1, "c", "d")[c(4, 3, 2)]
108+
testthat::expect_warning(
109+
testthat::expect_equal(c(doc1, doc2), teal_card("a", "b", "d", "c"), ignore_attr = TRUE)
110+
)
83111
})
84112

85-
it("with a `teal_card` and keeps original metadata", {
113+
it("with a `teal_card` - keeps original metadata", {
86114
doc <- teal_card("a", "b")
87115
metadata(doc) <- list(title = "A Title", a = "test")
88116
doc_result <- c(doc, teal_card("new content"))
89117
testthat::expect_identical(metadata(doc_result), list(title = "A Title", a = "test"))
90118
})
91119

92-
it("new `teal_card` and combines metadata and overwrites original", {
120+
it("new `teal_card` - combines metadata and overwrites original", {
93121
doc1 <- teal_card("a", "b")
94122
metadata(doc1) <- list(title = "A Title", a = "test")
95123
doc2 <- teal_card("new content")
@@ -118,22 +146,22 @@ testthat::describe("as.teal_card", {
118146
it("converts a simple list with each element being converted to a report content", {
119147
simple_list <- list("a", "b", "c")
120148
doc <- as.teal_card(simple_list)
121-
testthat::expect_identical(doc, teal_card("a", "b", "c"))
149+
testthat::expect_equal(doc, teal_card("a", "b", "c"), ignore_attr = TRUE)
122150
})
123151

124152
it("converts a custom list class with many elements into single-element-teal_card", {
125153
custom_list <- list("a", "b", "c", "d")
126154
class(custom_list) <- "extra class"
127155
doc <- as.teal_card(custom_list)
128-
testthat::expect_identical(doc, teal_card(custom_list))
156+
testthat::expect_equal(doc, teal_card(custom_list), ignore_attr = TRUE)
129157
})
130158

131159
it("converts a ggplot2 to a teal_card with only 1 report content", {
132160
testthat::skip_if_not_installed("ggplot2")
133161
plot <- ggplot2::ggplot(iris) +
134162
ggplot2::geom_point(ggplot2::aes(x = Sepal.Length, y = Sepal.Width))
135163
doc <- as.teal_card(plot)
136-
testthat::expect_identical(doc, teal_card(plot))
164+
testthat::expect_equal(doc, teal_card(plot), ignore_attr = TRUE)
137165
})
138166
})
139167

tests/testthat/test-teal_report-c.R

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ testthat::describe("c.teal_report combines", {
66
it("empty and non-empty teal_report by appending elements of teal_card", {
77
treport1 <- teal_report()
88
treport2 <- teal_report(teal_card = teal_card("Text 2"))
9-
10-
testthat::expect_identical(
9+
testthat::expect_equal(
1110
teal_card(c(treport1, treport2)),
12-
teal_card("Text 2")
11+
teal_card("Text 2"),
12+
ignore_attr = TRUE
1313
)
1414
})
1515

1616
it("two teal_report by combining elements of teal_card", {
1717
treport1 <- teal_report(teal_card = teal_card("Text 1"))
1818
treport2 <- teal_report(teal_card = teal_card("Text 2"))
19-
20-
testthat::expect_identical(
19+
testthat::expect_equal(
2120
teal_card(c(treport1, treport2)),
22-
teal_card("Text 1", "Text 2")
21+
teal_card("Text 1", "Text 2"),
22+
ignore_attr = TRUE
2323
)
2424
})
2525

@@ -29,9 +29,10 @@ testthat::describe("c.teal_report combines", {
2929
treport3 <- teal_report()
3030
treport4 <- teal_report(teal_card = teal_card("Text 2"))
3131

32-
testthat::expect_identical(
32+
testthat::expect_equal(
3333
teal_card(c(treport1, treport2, treport3, treport4)),
34-
teal_card("Text 1", "Text 2", "Text 2")
34+
teal_card("Text 1", "Text 2", "Text 2"),
35+
ignore_attr = TRUE
3536
)
3637
})
3738
})
Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
testthat::describe("keep_output stores the objects in teal_card", {
22
it("using eval_code and explicit reference", {
33
q <- eval_code(teal_report(), "a <- 1L;b <- 2L;c <- 3L", keep_output = "b")
4-
testthat::expect_identical(
4+
testthat::expect_equal(
55
teal_card(q),
66
teal_card(
77
code_chunk("a <- 1L;b <- 2L;c <- 3L"),
88
structure(2L, class = c("chunk_output", "integer"))
9-
)
9+
),
10+
ignore_attr = TRUE
1011
)
1112
})
1213

@@ -19,32 +20,35 @@ testthat::describe("keep_output stores the objects in teal_card", {
1920
},
2021
keep_output = "b"
2122
)
22-
testthat::expect_identical(
23+
testthat::expect_equal(
2324
teal_card(q),
2425
teal_card(
2526
code_chunk("a <- 1L\nb <- 2L\nc <- 3L"),
2627
structure(2L, class = c("chunk_output", "integer"))
27-
)
28+
),
29+
ignore_attr = TRUE
2830
)
2931
})
3032

3133
it("with multiple explicit object references", {
3234
q <- eval_code(teal_report(), "a <- 1L;b <- 2L;c <- 3L", keep_output = c("a", "b"))
33-
testthat::expect_identical(
35+
testthat::expect_equal(
3436
teal_card(q),
3537
teal_card(
3638
code_chunk("a <- 1L;b <- 2L;c <- 3L"),
3739
structure(1L, class = c("chunk_output", "integer")),
3840
structure(2L, class = c("chunk_output", "integer"))
39-
)
41+
),
42+
ignore_attr = TRUE
4043
)
4144
})
4245

4346
it("without explicit reference returing none", {
4447
q <- eval_code(teal_report(), "a <- 1L;b <- 2L;c <- 3L", keep_output = character(0L))
45-
testthat::expect_identical(
48+
testthat::expect_equal(
4649
teal_card(q),
47-
teal_card(code_chunk("a <- 1L;b <- 2L;c <- 3L"))
50+
teal_card(code_chunk("a <- 1L;b <- 2L;c <- 3L")),
51+
ignore_attr = TRUE
4852
)
4953
})
5054
})

0 commit comments

Comments
 (0)