Skip to content

Commit

Permalink
ggplot2 compatibility cleanup (#29)
Browse files Browse the repository at this point in the history
* leverage ggplot2::ggplot_add for more responsible composition
  • Loading branch information
dgkf authored Oct 10, 2022
1 parent 5a1cd97 commit 5c24be6
Show file tree
Hide file tree
Showing 19 changed files with 63 additions and 108 deletions.
19 changes: 9 additions & 10 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
Package: ggpackets
Title: Package Plot Layers for Easier Portability and Modularization
Version: 0.2.1.9000
Authors@R:
person("Doug", "Kelkhoff",
email = "[email protected]",
Version: 0.2.1
Authors@R:
person("Doug", "Kelkhoff",
email = "[email protected]",
role = c("aut", "cre"))
Description:
Description:
Create groups of 'ggplot2' layers that can be easily migrated from one plot
to another, reducing redundant code and improving the ability to format many
plots that draw from the same source 'ggpacket' layers.
Expand All @@ -15,9 +15,7 @@ Depends:
Imports:
utils,
methods,
tibble,
rlang,
crayon
rlang
Suggests:
testthat,
dplyr,
Expand All @@ -28,8 +26,9 @@ Suggests:
covr
LazyData: true
License: MIT + file LICENSE
RoxygenNote: 7.2.0
RoxygenNote: 7.2.1
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
VignetteBuilder: knitr
URL: https://dgkf.github.io/ggpackets/
URL: https://github.com/dgkf/ggpackets, https://dgkf.github.io/ggpackets/
BugReports: https://github.com/dgkf/ggpackets/issues
4 changes: 2 additions & 2 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Generated by roxygen2: do not edit by hand

S3method("+",gg)
S3method(format,ggpacket)
S3method(ggplot_add,ggpacket)
S3method(print,ggpacket)
export("%+%")
export(ggpacket)
importFrom(crayon,red)
importFrom(ggplot2,aes)
importFrom(ggplot2,ggplot)
importFrom(ggplot2,ggplot_add)
importFrom(ggplot2,ggplot_build)
importFrom(ggplot2,standardise_aes_names)
importFrom(ggplot2,waiver)
Expand Down
6 changes: 5 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# ggpackets (dev)
# ggpackets v0.2.1

* handle `+.gg` using recommended `ggplot2::ggplot_add` instead of intercepting
calls (#24, @dgkf)

* remove `crayon` package dependency, only used for console output of missing
aesthetics

# ggpackets v0.2.0

Expand Down
21 changes: 13 additions & 8 deletions R/ggcall.R
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#' Convert an expression into a call as a list of quosure components
#'
#'
#' @param x An expression to convert to a ggcall.
#' @param which The relative frame offset in which the expression should be
#' eventually evaluated.
#'
#' @importFrom rlang quos enquo quo_get_expr quo_set_env
#' @importFrom ggplot2 standardise_aes_names
#'
#' @keywords internal
#'
as_gg_call <- function(x, which = -3L) {
xexpr <- eval(bquote(
substitute(.(substitute(x)))),
Expand All @@ -20,7 +22,7 @@ as_gg_call <- function(x, which = -3L) {
if (".id" %in% names(xcall)[-1]) {
xids <- rlang::eval_tidy(xcall[[".id"]])
xcall <- xcall[c(1, 1 + which(names(xcall[-1]) != ".id"))]
}
}
xcallname <- infer_ggcall_name(rlang::quo_get_expr(xcall[[1]]))
} else {
xcall <- rlang::quo_set_env(rlang::enquo(xexpr), parent.frame(-which - 1L))
Expand All @@ -39,11 +41,13 @@ as_gg_call <- function(x, which = -3L) {


#' Label ggcall with function name if it can be deduced
#'
#'
#' @param expr An expression from which a call name should be inferred.
#'
#'
#' @keywords internal
#'
infer_ggcall_name <- function(expr) {
# TODO: prohibit names ambiguous with gg args with dots
# TODO: prohibit names ambiguous with gg args with dots
# (inherit.aes, na.rm, show.legend, fun.data, label.r)
if (is.name(expr) && grepl("\\w", expr)) as.character(expr)
else "layer"
Expand All @@ -52,10 +56,11 @@ infer_ggcall_name <- function(expr) {


#' Convert ggplot geom layers to friendly names
#'
#'
#' @param x A function name from which an id should be inferred.
#'
#'
#' @keywords internal
#'
infer_ggcall_id <- function(x) {
gsub("^(geom|stat)_", "", x)
}

2 changes: 1 addition & 1 deletion R/ggpacket.R
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ required_aesthetics.LayerInstance <- function(x) {
}

required_aesthetics.quosures <- function(x) {
aess <- .all_aesthetics
aess <- .all_aesthetics()
names(aess) <- paste0("..", aess, "..")

layer <- tryCatch(rlang::eval_tidy(x[[1]]), error = function(e) NULL)
Expand Down
1 change: 0 additions & 1 deletion R/ggpacket_index.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ subset_ggpacket.character <- function(x, i, ...) {
xs <- vapply(x@ggcalls, function(xi) any(i %in% attr(xi, "ids")), logical(1L))
x[xs, ...]
}

3 changes: 1 addition & 2 deletions R/ggpacket_show.R
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,11 @@ format_ggpacket_mapping.NULL <- function(x,
}

#' @importFrom utils capture.output
#' @importFrom crayon red
format_ggpacket_mapping.default <- function(x,
width = getOption("width", 80) * 0.9, missing_aes = character(0L)) {

x[missing_aes] <- " MISSING "
gsub("\" MISSING \"", crayon::red("<missing>"), utils::capture.output(x)[-1])
gsub("\" MISSING \"", "<missing>", utils::capture.output(x)[-1])
}

format_ggpacket_ggcalls <- function(x,
Expand Down
1 change: 1 addition & 0 deletions R/ggpackets.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#' plots that draw from the same source ggpacket layers.
#'
#' @examples
#' library(ggplot2)
#'
#' # Prep a tidy data.frame to plot with
#' airquality_long <- rbind(
Expand Down
33 changes: 4 additions & 29 deletions R/ggplot2_ext.R
Original file line number Diff line number Diff line change
@@ -1,36 +1,11 @@
#' ggplot2 internal gg addition method
#'
#' @param e1 Addition lhs
#' @param e2 Addition rhs
#'
.plus_gg <- getNamespace("ggplot2")[["+.gg"]]


#' Intercept ggplot2 ggproto plus operator
#'
#' @param e1 An object to add to a ggproto object.
#' @param e2 A ggproto object to add.
#'
#' @return A \code{ggplot2} object or \code{ggpacket}, dependent on whether
#' \code{e1} is a materialized \code{ggproto} object or a \code{ggpacket}.
#'
#' @importFrom methods new
#' @importFrom ggplot2 ggplot_add
#' @export
"+.gg" <- function(e1, e2) {
if (inherits(e2, "ggpacket"))
return(gg_plus_ggpacket(e1, e2))

if (!inherits(e1, "ggproto"))
return(.plus_gg(e1, e2))

methods::new(
"ggpacket",
ggpacket_call,
ggcalls = list(as_gg_call(e1), as_gg_call(e2))
)
ggplot_add.ggpacket <- function(object, plot, object_name) {
gg_plus_ggpacket(plot, object)
}



#' Lazy handler for ggplot addition
#'
#' @param e1 Addition lhs.
Expand Down
9 changes: 5 additions & 4 deletions R/utils_aesthetics.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#' Extracted .all_aesthetics from internal ggplot2 with hardcoded fallback
.all_aesthetics <- tryCatch({
.all_aesthetics <- function() {
tryCatch({
# attempt to stay current with ggplot .all_aesthetics upstream
get('.all_aesthetics', asNamespace('ggplot2'), inherits = FALSE)
get(".all_aesthetics", asNamespace("ggplot2"), inherits = FALSE)
}, error = function(e) {
# hard coded fallback in case upstream changes private variable name
# #est for fallback viability included in testthat tests
Expand All @@ -11,6 +12,7 @@
"upper", "vjust", "weight", "width", "x", "xend", "xmax", "xmin",
"xintercept", "y", "yend", "ymax", "ymin", "yintercept", "z")
})
}



Expand All @@ -33,7 +35,7 @@ handle_reset_mapping <- function(mapping) {
#' @param envir An environment in which the dot aesthetics should be evaluated.
#'
substitute_ggcall_dot_aes <- function(mapping, ggcall, envir = parent.frame()) {
aess <- .all_aesthetics
aess <- .all_aesthetics()
names(aess) <- ggplot2::standardise_aes_names(aess)

# add in mappings for alternative naming conventions before substitution
Expand Down Expand Up @@ -74,4 +76,3 @@ substitute_quote.quosure <- function(q, env = parent.frame()) {
# TODO: handle mixed quosure environments instead of retaining original
rlang::quo_set_expr(q, do.call(substitute, list(rlang::quo_squash(q), env)))
}

8 changes: 4 additions & 4 deletions R/zzz.R
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.onLoad <- function(libname, pkgname) {
# Some current base functions are used which are not included in older
# versions of R. These are provided through an 'Enhances' pacakge,
# "backports" but this dependency is unnecessary otherwise.
if (package_version(R.Version()) < package_version("3.5") &&
# versions of R. These are provided through an 'Enhances' package,
# "backports" but this dependency is unnecessary otherwise.
if (package_version(R.Version()) < package_version("3.5") &&
requireNamespace("backports")) {
backports::import(pkgname, "isFALSE")
}
}
}
1 change: 1 addition & 0 deletions man/as_gg_call.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 1 addition & 6 deletions man/dot-all_aesthetics.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 0 additions & 16 deletions man/dot-plus_gg.Rd

This file was deleted.

3 changes: 3 additions & 0 deletions man/ggpackets-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/infer_ggcall_id.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/infer_ggcall_name.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 0 additions & 20 deletions man/plus-.gg.Rd

This file was deleted.

15 changes: 11 additions & 4 deletions tests/testthat/test-ggpacket-show.R
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ test_that("ggpacket with a bound ggcall layer prints bound ggcall expression", {

test_that("ggpacket lacking required aesthetics indicates aesthetic missing", {
expect_match({
crayon::strip_style(paste(capture.output({
paste(capture.output({
ggpacket() + geom_line()
}), collapse = "\n"))
}), collapse = "\n")
}, c("`x` -> <missing>"))

expect_match({
crayon::strip_style(paste(capture.output({
paste(capture.output({
ggpacket() + geom_line()
}), collapse = "\n"))
}), collapse = "\n")
}, c("`y` -> <missing>"))
})

Expand All @@ -88,3 +88,10 @@ test_that("ggpacket including required aesthetics considers internal remappings"
))
})
})

test_that("show(<ggpacket>) behaviors identical to print for command line output", {
expect_identical(
capture.output(show(ggpacket(aes(y = test)) + geom_line(aes(x = ..y..)))),
capture.output(print(ggpacket(aes(y = test)) + geom_line(aes(x = ..y..))))
)
})

0 comments on commit 5c24be6

Please sign in to comment.