Skip to content

Commit cd54bfb

Browse files
committed
add Stat method for make_constructor()
1 parent 44cf2dc commit cd54bfb

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ S3method(limits,factor)
9292
S3method(limits,numeric)
9393
S3method(makeContext,dotstackGrob)
9494
S3method(make_constructor,Geom)
95+
S3method(make_constructor,Stat)
9596
S3method(merge_element,default)
9697
S3method(merge_element,element)
9798
S3method(merge_element,element_blank)

R/make-constructor.R

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ make_constructor.Geom <- function(x, ..., checks = exprs(), env = caller_env())
7575
args[param] <- draw_args[param]
7676
}
7777
}
78+
extra_args <- intersect(extra_args, names(args))
7879
missing_params <- setdiff(missing_params, names(args))
7980
if (length(missing_params) > 0) {
8081
cli::cli_warn(
@@ -122,5 +123,90 @@ make_constructor.Geom <- function(x, ..., checks = exprs(), env = caller_env())
122123
# We encapsulate rlang::list2
123124
new_env <- new_environment(list(list2 = list2), env)
124125

125-
new_function(fmls, body, env = new_env)
126+
new_function(fmls, body, new_env)
126127
}
128+
129+
#' @export
130+
#' @rdname make_constructor
131+
make_constructor.Stat <- function(x, ..., checks = exprs(), env = caller_env()) {
132+
# Check that we can independently find the stat
133+
stat <- gsub("^stat_", "", snake_class(x))
134+
check_subclass(stat, "Stat", env = env)
135+
136+
# Split additional arguments into required and extra ones
137+
args <- enexprs(...)
138+
fixed_fmls_names <- c("mapping", "data", "geom", "position", "...",
139+
"na.rm", "show.legend", "inherit.aes")
140+
extra_args <- setdiff(names(args), fixed_fmls_names)
141+
if ("stat" %in% extra_args) {
142+
cli::cli_abort("{.arg stat} is a reversed argument.")
143+
}
144+
145+
known_params <-
146+
unique(c(names(args), fixed_fmls_names, "flipped_aes", x$aesthetics()))
147+
missing_params <- setdiff(x$parameters(), known_params)
148+
149+
# Fill in missing parameters from the compute methods
150+
if (length(missing_params) > 0) {
151+
compute_args <- ggproto_formals(x$compute_panel)
152+
if ("..." %in% names(compute_args)) {
153+
compute_args <- ggproto_formals(x$compute_group)
154+
}
155+
params <- intersect(missing_params, names(compute_args))
156+
extra_args <- c(extra_args, params)
157+
for (param in params) {
158+
if (!identical(compute_args[[param]], missing_arg())) {
159+
args[param] <- compute_args[param]
160+
}
161+
}
162+
extra_args <- intersect(extra_args, names(args))
163+
missing_params <- setdiff(missing_params, names(args))
164+
if (length(missing_params) > 0) {
165+
cli::cli_warn(
166+
"In {.fn stat_{stat}}: please consider providing default values for: \\
167+
{missing_params}."
168+
)
169+
}
170+
}
171+
172+
# Build function formals
173+
fmls <- pairlist2(
174+
mapping = args$mapping,
175+
data = args$data,
176+
geom = args$geom %||% cli::cli_abort("{.arg geom} is required."),
177+
position = args$position %||% "identity",
178+
`...` = missing_arg(),
179+
!!!args[extra_args],
180+
na.rm = args$na.rm %||% FALSE,
181+
show.legend = args$show.legend %||% NA,
182+
inherit.aes = args$inherit.aes %||% TRUE
183+
)
184+
185+
# Construct params for the `layer(params)` argument
186+
params <- exprs(!!!syms(c("na.rm", extra_args)), .named = TRUE)
187+
params <- call2("list2", !!!params, quote(...))
188+
189+
# Construct rest of `layer()` call
190+
layer_args <- syms(setdiff(fixed_fmls_names, c("...", "na.rm")))
191+
layer_args <- append(layer_args, list(stat = stat), after = 3)
192+
layer_args <- exprs(!!!layer_args, params = !!params, .named = TRUE)
193+
body <- call2("layer", !!!layer_args)
194+
195+
# Prepend any checks
196+
if (length(exprs) > 0) {
197+
lang <- vapply(checks, is_call, logical(1))
198+
if (!all(lang)) {
199+
cli::cli_abort(
200+
"{.arg checks} must be a list of calls, such as one constructed \\
201+
with {.fn rlang::exprs}."
202+
)
203+
}
204+
}
205+
body <- call2("{", !!!checks, body)
206+
207+
# We encapsulate rlang::list2
208+
new_env <- new_environment(list(list2 = list2), env)
209+
210+
new_function(fmls, body, new_env)
211+
}
212+

0 commit comments

Comments
 (0)