Skip to content

Allow addition of custom objects #2309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 30, 2017
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ S3method("+",gg)
S3method("[",uneval)
S3method("[[",ggproto)
S3method(.DollarNames,ggproto)
S3method(add_to_ggplot,default)
S3method(as.character,uneval)
S3method(as.list,ggproto)
S3method(autolayer,default)
Expand Down Expand Up @@ -201,6 +202,7 @@ export(StatSummaryBin)
export(StatSummaryHex)
export(StatUnique)
export(StatYdensity)
export(add_to_ggplot)
export(aes)
export(aes_)
export(aes_all)
Expand Down
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# ggplot2 2.2.1.9000

* Custom objects can now be added using `+` if a `add_to_ggplot` method has been
defined for the class of the object (@thomasp85).

* Fixed bug when setting strips to `element_blank()` (@thomasp85).

* Strips gain margins on all sides by default. This means that to fully justify
Expand Down
25 changes: 22 additions & 3 deletions R/plot-construction.r
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ add_ggplot <- function(p, object, objectname) {
} else if (is.facet(object)) {
p$facet <- object
p
} else if (is.list(object)) {
} else if (is.list(object) && is.null(attr(object, 'class'))) {
for (o in object) {
p <- p %+% o
}
Expand All @@ -97,9 +97,28 @@ add_ggplot <- function(p, object, objectname) {
new_labels <- defaults(mapping, default)
p$labels <- defaults(p$labels, new_labels)
} else {
stop("Don't know how to add ", objectname, " to a plot",
call. = FALSE)
p <- add_to_ggplot(object, p, objectname)
}
set_last_plot(p)
p
}
#' Add custom objects to ggplot
#'
#' This generic allows you to add your own methods for adding custom objects to
#' a ggplot with [+.gg].
#'
#' @param object An object to add to the plot
#' @param p The ggplot object to add `object` to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think plot and object_name would be better argument names

#' @param objectname The name of the object to add
#'
#' @return A modified ggplot object
#'
#' @keywords internal
#' @export
add_to_ggplot <- function(object, p, objectname) {
UseMethod("add_to_ggplot")
}
#' @export
add_to_ggplot.default <- function(object, p, objectname) {
stop("Don't know how to add ", objectname, " to a plot", call. = FALSE)
}
23 changes: 23 additions & 0 deletions man/add_to_ggplot.Rd

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

16 changes: 16 additions & 0 deletions tests/testthat/test-plot-construction.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
context("plot-construction")

test_that("Custom object cannot be added without generic", {
p <- ggplot()
custom_object <- structure(list(), class = 'test_object')
expect_error(p + custom_object, "Don't know how to add custom_object to a plot")
})

test_that("Methods can be defined for adding custom objects", {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can define the method inside the test, just to make things slightly cleaner.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason I’m having problems getting dispatch to work correctly in the test. Moving it out fixed it locally but it still throws an error on travis..?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any idea why methods defined inside test_that expressions are not being dispatched to?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's something to do with the weird way in which S3 dispatch happens. I forget all the details, but just move it outside for now.

p <- ggplot()
custom_object <- structure(list(), class = 'test_object')
add_to_ggplot.test_object <- function(object, p, objectname) {
10
}
expect_equal(p + custom_object, 10)
})