roxygen2 icon indicating copy to clipboard operation
roxygen2 copied to clipboard

Documenting R6 classes that are generated using function calls.

Open sebffischer opened this issue 1 year ago • 10 comments

I would like to be able to document R6 classes that were generated by function calls. One problem I encounter is that I am not sure how I can document methods when generating R6 classes this way. E.g. in the example below, is it possible to document the method initialize for the R6 class generator A ?

The problem is

library(R6)

r6_gen_gen = function(class, name) {
  R6Class(class,
    public = list(initialize = function(name) self$name = name, name = NULL)
  )
}

A = r6_gen_gen("A", "Tom")

Created on 2022-10-19 by the reprex package (v2.0.1)

sebffischer avatar Oct 19 '22 08:10 sebffischer

This is difficult, because roxygen needs an object of the class to be able to look up superclasses, methods, etc. So I am afraid that currently the only way to document your class is the manual way, i.e. manually create sections for methods, etc. inside the documentation of the r6_gen_gen() function.

gaborcsardi avatar Oct 19 '22 08:10 gaborcsardi

Why is it not possible to look up superclasses? The object A is a standard R6ClassGenerator, no?

It is for example possible to document the fields of A. The problem I think is that in the documentation of the methods, there is no identifier what method is documented by the corresponding @description block in the roxygen2 documentation itself so I presume roxygen looks at the source code and inspects the name of the function that follows each @description block.

To make clearer what I mean: In the code below, the documentation of the field name knows that it documents the field name without looking at the source code, whereas the description of initialize has to look at the source code, to know to which method it belongs.

A = R6::R6Class("A",
  public = list(
    #' @field name The name
    name = NULL,
    #' @description
    #' Initializes an object.
    initialize = function() print("Hallo!")
  )
)

Created on 2022-10-19 by the reprex package (v2.0.1)

sebffischer avatar Oct 19 '22 08:10 sebffischer

Maybe a solution would be to allow for a @method so that the following would work:

r6_gen_gen = function(class, name) {
  R6::R6Class(class,
    public = list(initialize = function(name) self$name = name, name = NULL)
  )
}

#' @title A
#' @description Blabla
#' @field name the name
#' @method initialize Creates a new object.
#' @export
A = r6_gen_gen("A", "Tom")

Created on 2022-10-19 by the reprex package (v2.0.1)

sebffischer avatar Oct 19 '22 08:10 sebffischer

Right, if you actually create the A object and put it in the package namespace, then we could look up the superclasses and also methods. I assumed that you don't want to put objects of the class into the package, since you want to avoid class objects in the package.

Alas, roxygen2 still cannot handle this currently, because it uses the source code of the class to match docs to methods.

Maybe your suggestion can be implemented, but I am not convinced that it is common that you have objects in a package, but not classes. So I don't think that we'll assign high priority to this.

gaborcsardi avatar Oct 19 '22 08:10 gaborcsardi

But A is a class, note that r6_gen_gen generates a class and not an instance of the class.

The scenario in which this is useful is when one wants to create many classes that are very similar to one another but one still wants a different documentation for each class.

sebffischer avatar Oct 19 '22 08:10 sebffischer

Like in the code below, I would like to generate documentation for the classes A, B, C, ... (which all have a slightly different initialize method)

r6_gen_gen = function(class, x) {
  R6::R6Class(class,
    public = list(initialize = function() self$name = x, name = NULL)
  )
}

#' @export
A = r6_gen_gen("A", "Tom")
#' @export
B = r6_gen_gen("B", "Lydia")
#' @export
C = r6_gen_gen("C", "Swathy")
...

sebffischer avatar Oct 19 '22 09:10 sebffischer

Right, sorry, I understand it now.

gaborcsardi avatar Oct 19 '22 09:10 gaborcsardi

I could have described it better as well! Thanks for taking the time :)

sebffischer avatar Oct 19 '22 09:10 sebffischer

It would already help if there was the option to set r6 = TRUE or r6 = FALSE in a roxygen block and not only as one global option in the DESCRIPTION

sebffischer avatar Oct 19 '22 16:10 sebffischer

You can do something like this:

#' @name myclass
#' ...
NULL

myclass <- r6_gen()

to avoid the R6 docs for myclass.

gaborcsardi avatar Oct 19 '22 17:10 gaborcsardi