Reputation: 13372
I'm writing a package with an R6 class that has several methods. I would like to be able to generate documentation for both, class and methods. For the below example, I would like to be able to access the docs with ?Person
for the class and ?set_hair
, for the method. Here my example class:
#' This is my Person class
#' @title Person Class
#' @docType class
#' @description Person class description
#' @field name Name of the person
#' @field hair Hair colour
#'
#' @section Methods:
#' \describe{
#' \item{set_hair Set the hair color}
#' }
#'
#' @examples
#' Person$new(name="Bill", hair="Blond")
#' @export
Person <- R6::R6Class("Person",
public = list(
name = NULL,
hair = NULL,
initialize = function(name = NA, hair = NA) {
self$name <- name
self$hair <- hair
},
# '@name set_hair
# '@param val: hair colour
set_hair = function(val) {
self$hair <- val
},
)
)
Running roxygenise()
, the annotations above the method bodies are not rendered at all, so the only information I specify in @section Methods
is in the docs.
Since I have over 50 class methods, it would be much nicer if I can access the method docs with ?methodname
sepeartly. I found some some posts on this (Documenting R6 classes and methods within R package in RStudio, https://github.com/klutometis/roxygen/issues/306), but it seems to me that this is not supported for R6 classes.
What would be the best way to document my class methods separately?
Upvotes: 5
Views: 3417
Reputation: 400
This is an old post and you might have solved your question long ago. But it's not added here so in case someone need the solution, it will be:
#' This is my Person class
#' @description Person class description
#' @field name Name of the person
#' @field hair Hair colour
#'
#' @examples
#' Person$new(name="Bill", hair="Blond")
#' @export
Person <- R6::R6Class("Person",
public = list(
name = NULL,
hair = NULL,
#' @description
#' Create a person
#' @param name Name of the person
#' @param hair Hair colour
initialize = function(name = NA, hair = NA) {
self$name <- name
self$hair <- hair
},
#' @description Set hair
#' @param val Hair colour
set_hair = function(val) {
self$hair <- val
},
)
)
Upvotes: 6
Reputation: 168
The discussion on the github-raised issue linked in user2554330's comment above indicates that separated out documentation is not something on the to-do list for roxygen, as it does not match traditional style for method documentation. That said, I've still found it useful, and have been using a workaround.
One partial solution is to create functional wrappers. This is a semi-manual process that can be cumbersome given lots of methods (as in your case), but it does enable clear and semi-automated documentation for R6 methods in separate docs. Using the person
example, here is the would-be implementation of a document-able wrapper in roxygen2:
#` Method for setting hair
#`
#` @param person a person class object
#` @param val hair color
#`
#` @return nothing; modifies \code{person}
#` @export
#`
#` @examples
#` bill <- Person$new(name="Bill", hair="Blond")
#` bill$set_hair("InspiredRed")
#` bill$hair
#` set_hair(bill, "MetalBlack")
#` bill$hair
set_hair <- function(person, val){
person$set_hair(val)
invisible()
}
The result would be two separate .Rd
files, one for the person
class, and one for the set_hair
method, both accessible with ?
.
The result has an added advantage in that a call closer to functional form may be preferred by most R users, since this is closer to how most R syntax is set up. Both person$set_hair(val)
and set_hair(person, val)
will produce the same result without any need for explicit assignment, maintaining R6's advantages while adding minimal overhead.
EDIT:
After bringing this up with a few people, I've anecdotally found more preference for having actual functional form wrappers--ones which sacrifice the reference component in favor of a functional approach, as it is nearer to R's "norm." In this case, the approach still provides the same documentation advantages, while referential method calls are still available via $
. However, additional emphasis should be made on the existence of the referential method approach when writing the wrapper documentation.
#` Clones person and changes hair
#`
#` @param person a person class object
#` @param val hair color
#`
#` @return nothing; modifies \code{person}
#` @export
#`
#` @details This creates a new person with the same characteristics as the \code{person}
#' provided, except with new hair. To update the original person's hair by reference,
#' use \code{person$set_hair()}.
#`
#` @examples
#` bill <- Person$new(name="Bill", hair="Blond")
#` bill$set_hair("InspiredRed")
#` bill$hair
#` set_hair(bill, "MetalBlack")
#` bill$hair
set_hair <- function(person, val){
personNew <- person.clone()
personNew$set_hair(val)
invisible(personNew) # invisible assuming no print method, but you probably want one
}
Of course, which route to take with these wrappers depends on your specific application's speed and memory requirements. The functional approach may create enough of a hindrance to not be viable.
Upvotes: 2