Skip to content

Commit 0183011

Browse files
teunbrandpaleolimbot
authored andcommitted
Type stability for scale_apply() (#3711, closes #3688)
* More type stability for scale_apply() * Unit test scale_apply() preserves class and attributes
1 parent 3e3abd9 commit 0183011

File tree

2 files changed

+53
-4
lines changed

2 files changed

+53
-4
lines changed

R/layout.R

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,7 @@ scale_apply <- function(data, vars, method, scale_id, scales) {
305305
pieces <- lapply(seq_along(scales), function(i) {
306306
scales[[i]][[method]](data[[var]][scale_index[[i]]])
307307
})
308-
# Join pieces back together, if necessary
309-
if (!is.null(pieces)) {
310-
unlist(pieces)[order(unlist(scale_index))]
311-
}
308+
o <- order(unlist(scale_index))[seq_len(sum(lengths(pieces)))]
309+
do.call("c", pieces)[o]
312310
})
313311
}

tests/testthat/test-scales.r

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,54 @@ test_that("limits with NA are replaced with the min/max of the data for continuo
319319
expect_equal(make_scale(limits = c(1, NA), data = 1:5)$get_limits(), c(1, 5))
320320
expect_equal(make_scale(limits = c(NA, 5), data = 1:5)$get_limits(), c(1, 5))
321321
})
322+
323+
test_that("scale_apply preserves class and attributes", {
324+
df <- data_frame(
325+
x = structure(c(1, 2), foo = "bar", class = c("baz", "numeric")),
326+
y = c(1, 1),
327+
z = c("A", "B")
328+
)
329+
330+
# Functions to make the 'baz'-class more type stable
331+
`c.baz` <- function(...) {
332+
dots <- list(...)
333+
attris <- attributes(dots[[1]])
334+
x <- do.call("c", lapply(dots, unclass))
335+
attributes(x) <- attris
336+
x
337+
}
338+
`[.baz` <- function(x, i) {
339+
attris <- attributes(x)
340+
x <- unclass(x)[i]
341+
attributes(x) <- attris
342+
x
343+
}
344+
345+
plot <- ggplot(df, aes(x, y)) +
346+
scale_x_continuous() +
347+
# Facetting such that 2 x-scales will exist, i.e. `x` will be subsetted
348+
facet_grid(~ z, scales = "free_x")
349+
plot <- ggplot_build(plot)
350+
351+
# Perform identity transformation via `scale_apply`
352+
out <- with_bindings(scale_apply(
353+
df, "x", "transform", 1:2, plot$layout$panel_scales_x
354+
)[[1]], `c.baz` = `c.baz`, `[.baz` = `[.baz`, .env = global_env())
355+
356+
# Check class preservation
357+
expect_is(out, "baz")
358+
expect_is(out, "numeric")
359+
360+
# Check attribute preservation
361+
expect_identical(attr(out, "foo"), "bar")
362+
363+
# Negative control: non-type stable classes don't preserve attributes
364+
class(df$x) <- "foobar"
365+
366+
out <- with_bindings(scale_apply(
367+
df, "x", "transform", 1:2, plot$layout$panel_scales_x
368+
)[[1]], `c.baz` = `c.baz`, `[.baz` = `[.baz`, .env = global_env())
369+
370+
expect_false(inherits(out, "foobar"))
371+
expect_null(attributes(out))
372+
})

0 commit comments

Comments
 (0)