Skip to content

Commit 7fce100

Browse files
committed
Reimplement S3 <uneval> into S7 <mapping>
1 parent b4163e0 commit 7fce100

File tree

13 files changed

+51
-61
lines changed

13 files changed

+51
-61
lines changed

NAMESPACE

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
S3method("$","ggplot2::theme")
44
S3method("$",ggproto)
55
S3method("$",ggproto_parent)
6-
S3method("$<-",uneval)
6+
S3method("$<-","ggplot2::mapping")
77
S3method("+",gg)
8+
S3method("[","ggplot2::mapping")
89
S3method("[",mapped_discrete)
9-
S3method("[",uneval)
10+
S3method("[<-","ggplot2::mapping")
1011
S3method("[<-",mapped_discrete)
11-
S3method("[<-",uneval)
1212
S3method("[[",ggproto)
13-
S3method("[[<-",uneval)
13+
S3method("[[<-","ggplot2::mapping")
1414
S3method(.DollarNames,ggproto)
1515
S3method(as.data.frame,mapped_discrete)
1616
S3method(as.list,ggproto)
@@ -66,7 +66,6 @@ S3method(ggplot_add,data.frame)
6666
S3method(ggplot_add,default)
6767
S3method(ggplot_add,labels)
6868
S3method(ggplot_add,list)
69-
S3method(ggplot_add,uneval)
7069
S3method(ggplot_build,ggplot)
7170
S3method(ggplot_build,ggplot_built)
7271
S3method(ggplot_gtable,ggplot_built)
@@ -105,14 +104,14 @@ S3method(predictdf,default)
105104
S3method(predictdf,glm)
106105
S3method(predictdf,locfit)
107106
S3method(predictdf,loess)
107+
S3method(print,"ggplot2::mapping")
108108
S3method(print,element)
109109
S3method(print,ggplot)
110110
S3method(print,ggplot2_bins)
111111
S3method(print,ggproto)
112112
S3method(print,ggproto_method)
113113
S3method(print,rel)
114114
S3method(print,theme)
115-
S3method(print,uneval)
116115
S3method(scale_type,Date)
117116
S3method(scale_type,POSIXt)
118117
S3method(scale_type,character)

R/aes.R

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ NULL
4646
#' 'AsIs' variables.
4747
#'
4848
#' @family aesthetics documentation
49-
#' @return A list with class `uneval`. Components of the list are either
50-
#' quosures or constants.
49+
#' @return An S7 object representing a list with class `mapping`. Components of
50+
#' the list are either quosures or constants.
5151
#' @export
5252
#' @examples
5353
#' aes(x = mpg, y = wt)
@@ -105,13 +105,21 @@ aes <- function(x, y, ...) {
105105
inject(aes(!!!args))
106106
})
107107

108-
aes <- new_aes(args, env = parent.frame())
109-
rename_aes(aes)
108+
mapping(rename_aes(args), env = parent.frame())
110109
}
111110

111+
mapping <- S7::new_class(
112+
"mapping", parent = S7::new_S3_class("gg"),
113+
constructor = function(x, env = globalenv()) {
114+
check_object(x, is.list, "a {.cls list}")
115+
x <- lapply(x, new_aesthetic, env = env)
116+
S7::new_object(x)
117+
}
118+
)
119+
112120
#' @export
113121
#' @rdname is_tests
114-
is.mapping <- function(x) inherits(x, "uneval")
122+
is.mapping <- function(x) S7::S7_inherits(x, mapping)
115123

116124
# Wrap symbolic objects in quosures but pull out constants out of
117125
# quosures for backward-compatibility
@@ -130,14 +138,9 @@ new_aesthetic <- function(x, env = globalenv()) {
130138

131139
x
132140
}
133-
new_aes <- function(x, env = globalenv()) {
134-
check_object(x, is.list, "a {.cls list}")
135-
x <- lapply(x, new_aesthetic, env = env)
136-
structure(x, class = "uneval")
137-
}
138141

139142
#' @export
140-
print.uneval <- function(x, ...) {
143+
`print.ggplot2::mapping` <- function(x, ...) {
141144
cat("Aesthetic mapping: \n")
142145

143146
if (length(x) == 0) {
@@ -153,25 +156,22 @@ print.uneval <- function(x, ...) {
153156
}
154157

155158
#' @export
156-
"[.uneval" <- function(x, i, ...) {
157-
new_aes(NextMethod())
159+
"[.ggplot2::mapping" <- function(x, i, ...) {
160+
mapping(NextMethod())
158161
}
159162

160163
# If necessary coerce replacements to quosures for compatibility
161164
#' @export
162-
"[[<-.uneval" <- function(x, i, value) {
163-
new_aes(NextMethod())
165+
"[[<-.ggplot2::mapping" <- function(x, i, value) {
166+
mapping(NextMethod())
164167
}
165168
#' @export
166-
"$<-.uneval" <- function(x, i, value) {
167-
# Can't use NextMethod() because of a bug in R 3.1
168-
x <- unclass(x)
169-
x[[i]] <- value
170-
new_aes(x)
169+
"$<-.ggplot2::mapping" <- function(x, i, value) {
170+
mapping(NextMethod())
171171
}
172172
#' @export
173-
"[<-.uneval" <- function(x, i, value) {
174-
new_aes(NextMethod())
173+
"[<-.ggplot2::mapping" <- function(x, i, value) {
174+
mapping(NextMethod())
175175
}
176176

177177
#' Standardise aesthetic names
@@ -212,8 +212,7 @@ substitute_aes <- function(x, fun = standardise_aes_symbols, ...) {
212212
x <- lapply(x, function(aesthetic) {
213213
as_quosure(fun(quo_get_expr(aesthetic), ...), env = environment(aesthetic))
214214
})
215-
class(x) <- "uneval"
216-
x
215+
mapping(x)
217216
}
218217
# x is a quoted expression from inside aes()
219218
standardise_aes_symbols <- function(x) {
@@ -311,7 +310,7 @@ aes_ <- function(x, y, ...) {
311310
}
312311
}
313312
mapping <- lapply(mapping, as_quosure_aes)
314-
structure(rename_aes(mapping), class = "uneval")
313+
mapping(rename_aes(mapping))
315314
}
316315

317316
#' @rdname aes_
@@ -337,7 +336,7 @@ aes_string <- function(x, y, ...) {
337336
new_aesthetic(x, env = caller_env)
338337
})
339338

340-
structure(rename_aes(mapping), class = "uneval")
339+
mapping(rename_aes(mapping))
341340
}
342341

343342
#' @export
@@ -358,10 +357,7 @@ aes_all <- function(vars) {
358357

359358
# Quosure the symbols in the empty environment because they can only
360359
# refer to the data mask
361-
structure(
362-
lapply(vars, function(x) new_quosure(as.name(x), emptyenv())),
363-
class = "uneval"
364-
)
360+
mapping(lapply(vars, function(x) new_quosure(as.name(x), emptyenv())))
365361
}
366362

367363
#' Automatic aesthetic mapping
@@ -396,7 +392,7 @@ aes_auto <- function(data = NULL, ...) {
396392
aes <- c(aes, args[names(args) != "data"])
397393
}
398394

399-
structure(rename_aes(aes), class = "uneval")
395+
mapping(rename_aes(aes))
400396
}
401397

402398
mapped_aesthetics <- function(x) {

R/layer.R

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ validate_mapping <- function(mapping, call = caller_env()) {
213213
}
214214

215215
# For backward compatibility with pre-tidy-eval layers
216-
new_aes(mapping)
216+
mapping(mapping)
217217
}
218218

219219
Layer <- ggproto("Layer", NULL,
@@ -265,7 +265,7 @@ Layer <- ggproto("Layer", NULL,
265265
setup_layer = function(self, data, plot) {
266266
# For annotation geoms, it is useful to be able to ignore the default aes
267267
if (isTRUE(self$inherit.aes)) {
268-
self$computed_mapping <- defaults(self$mapping, plot$mapping)
268+
self$computed_mapping <- mapping(defaults(self$mapping, plot$mapping))
269269

270270
# Inherit size as linewidth from global mapping
271271
if (self$geom$rename_size &&
@@ -275,8 +275,6 @@ Layer <- ggproto("Layer", NULL,
275275
self$computed_mapping$size <- plot$mapping$size
276276
deprecate_soft0("3.4.0", I("Using `size` aesthetic for lines"), I("`linewidth`"))
277277
}
278-
# defaults() strips class, but it needs to be preserved for now
279-
class(self$computed_mapping) <- "uneval"
280278
} else {
281279
self$computed_mapping <- self$mapping
282280
}

R/plot-construction.R

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,8 @@ ggplot_add.Guides <- function(object, plot, object_name) {
156156
}
157157
plot
158158
}
159-
#' @export
160-
ggplot_add.uneval <- function(object, plot, object_name) {
161-
plot$mapping <- defaults(object, plot$mapping)
162-
# defaults() doesn't copy class, so copy it.
163-
class(plot$mapping) <- class(object)
159+
S7::method(ggplot_add, mapping) <- function(object, plot, object_name) {
160+
plot$mapping <- mapping(defaults(object, plot$mapping))
164161
plot
165162
}
166163
#' @export

R/quick-plot.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ qplot <- function(x, y, ..., data, facets = NULL, margins = FALSE,
7878
is_constant <- (!names(exprs) %in% ggplot_global$all_aesthetics) |
7979
vapply(exprs, quo_is_call, logical(1), name = "I")
8080

81-
mapping <- new_aes(exprs[!is_missing & !is_constant], env = parent.frame())
81+
mapping <- mapping(exprs[!is_missing & !is_constant], env = parent.frame())
8282

8383
consts <- exprs[is_constant]
8484

R/summarise-plot.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ summarise_coord <- function(p) {
124124
summarise_layers <- function(p) {
125125
check_inherits(p, "ggplot_built")
126126

127-
# Default mappings. Make sure it's a regular list instead of an uneval
127+
# Default mappings. Make sure it's a regular list instead of a mapping
128128
# object.
129129
default_mapping <- unclass(p$plot$mapping)
130130

man/aes.Rd

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/_snaps/aes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454

5555
Don't know how to get alternative usage for `foo`.
5656

57-
# new_aes() checks its inputs
57+
# mapping() checks its inputs
5858

5959
`x` must be a <list>, not an integer vector.
6060

tests/testthat/_snaps/fortify.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# fortify.default proves a helpful error with class uneval
1+
# fortify.default proves a helpful error with mapping class
22

3-
`data` must be a <data.frame>, or an object coercible by `fortify()`, or a valid <data.frame>-like object coercible by `as.data.frame()`, not a <uneval> object.
3+
`data` must be a <data.frame>, or an object coercible by `fortify()`, or a valid <data.frame>-like object coercible by `as.data.frame()`, not a <ggplot2::mapping> object.
44
i Did you accidentally pass `aes()` to the `data` argument?
55

66
# fortify.default can handle healthy data-frame-like objects

tests/testthat/test-add.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
test_that("mapping class is preserved when adding uneval objects", {
1+
test_that("mapping class is preserved when adding mapping objects", {
22
p <- ggplot(mtcars) + aes(wt, mpg)
3-
expect_identical(class(p$mapping), "uneval")
3+
expect_s7_class(p$mapping, mapping)
44
})

0 commit comments

Comments
 (0)