Roxygen: how to document method without cluttering the help index?
Asked Answered
U

2

6

I'm trying to document methods in the same file as the generic. I want the usage section to contain the method, but I do not want an alias generated for the method. This is because I have many methods for the generic and I'd like to keep the index relatively clean.

I've tried both @rdname and @describeIn but both seem to automatically generate an \alias tag which then shows up in the index. I can get the desired result by manually editing the Rd file and removing the \alias{} entry, but that isn't really sustainable.

UPDATE: Just noticed the following from R CMD Check:

Functions with \usage entries need to have the appropriate \alias entries, and all their arguments documented.

So maybe what I'm looking for is not even legal.

Unipersonal answered 5/3, 2015 at 2:8 Comment(10)
All of a sudden all the method index entries vanished. I have no idea what I did different.Unipersonal
Nevermind, they are back after re-install.Unipersonal
S3 or S4? It matters.Kei
@hadley, I think these were S3 methods I was dealing with, but I use S4 regularly as well so I would be interested in both solutions if they exist.Unipersonal
You don't need to document S3 methods, so just export them without docsKei
@hadley, the problem is that each method has different arguments (those above and beyond the ones defined in the generic), and those ideally would show up in the usage section.Unipersonal
Then there's no way around it as far as I know. Every documented function must be listed in the aliases, and every alias is listed in the index.Kei
@hadley, that's what I feared; thanks for taking the time with this though.Unipersonal
But maybe you're thinking about this the wrong way - the point of the index is to be comprehensive. I don't think it makes sense to worry about cluttering it up.Kei
@Tim dnorm appears in the index of the stats package for meKei
F
4

You can use a multi-line @useage like so:

#' a generic called foo
#' 
#' @param x the only named parameter
#' 
#' @usage 
#' # you can call `foo()` this way
#' foo(x, ..., [n, ybar,])
#' # or  this way
#' foo(x, ..., na.rm = FALSE, details = FALSE)
#' # or even  this way
#' foo(x, ..., [n, ybar,] na.rm = FALSE, details = FALSE)

foo  <-  function(x,...)
    return('hello world')

which produces the following foo.Rd file:

% Generated by roxygen2 (4.1.0): do not edit by hand
% Please edit documentation in R/peb-utils.r
\name{foo}
\alias{foo}
\title{a generic called foo}
\usage{
# you can call `foo()` this way
foo(x, ..., [n, ybar,])
# or  this way
foo(x, ..., na.rm = FALSE, details = FALSE)
# or even  this way
foo(x, ..., [n, ybar,] na.rm = FALSE, details = FALSE)
}
\arguments{
\item{x}{the only named parameter}
}
\description{
a generic called foo
}

Unfortunately, this does raise some warnings in the R CMD check:

* checking for code/documentation mismatches ... WARNING
Codoc mismatches from documentation object 'foo':
foo
  Code: function(x, ...)
  Docs: function(x, ..., na.rm = FALSE, details = FALSE)
  Argument names in docs not in code:
    na.rm details

* checking Rd \usage sections ... WARNING

Undocumented arguments in documentation object 'foo'
  '...' 'na.rm' 'details'

Bad \usage lines found in documentation object 'foo':
  foo(x, ..., [n, ybar,])
  foo(x, ..., [n, ybar,] na.rm = FALSE, details = FALSE)

Functions with \usage entries need to have the appropriate \alias
entries, and all their arguments documented.
The \usage entries must correspond to syntactically valid R code.
See the chapter 'Writing R documentation files' in the 'Writing R
Flask answered 5/3, 2015 at 6:0 Comment(5)
Thanks, this is definitely better than manually editing the file (+1); in your experience does this pass R CMD check?Unipersonal
Unfortunately this does raise some warnings in the R CMD check (See edits). I've only used this document style on a package that I shared within my work group and not on CRAN.Flask
Just a thought, A multi-line usage statement may still work if you explicitly provide empty usage strings to your S3 methods #' @usage \n, which might prevent the warnings about Functions with \usage entries need to have the appropriate \alias entries, and all their arguments documented. That would be my next try, but I don't have time to try it myself just now.Flask
Thanks for your help. I'll leave this unanswered for now in case someone comes up with something different.Unipersonal
This must be possible, as the documentation for t.test does exactly that. There is an alias for every method in the Rd file but they are not listed in the index. But how can that be achieved?Almondeyed
S
3

Here's a way to use roxygen 5.0.0+ to export generic function methods without creating aliases at the same time, so that the methods aren't listed in the index but are still documented properly in the help page of the generic function. The advantages over the method proposed by @Jthorpe are two-fold:

  1. You don't have to manually spell out the calling signature for methods (after all, you've already done so by defining the method in the first place).

  2. The techniques employed are generally useful for manipulating Rd-file structure with roxygen, beyond the facility provided by @-tags.

First, export your generic/methods in the usual way. Notice that there is no @rdname, so aliases won't be created.

#' @export
my_generic <- function(x, ...) UseMethod("my_generic")

#' @export
my_generic.default <- function(x, a = NULL, ...) "Default method"

#' @export
my_generic.numeric <- function(x, a = 0, ...) "Numeric method"

Next, follow that with a roxygen block for my_generic. The noteworthy features of this block are: 1) an alias for the generic function will be created (by @name), but not for any of its methods; 2) the @evalRd tag (available since roxygen 5.0.0) evaluates its code to create the \usage part of the Rd file, programatically.

#' My generic function
#'
#' @evalRd rd_s3_usage("my_generic", "default", "numeric")
#' @param x Some object.
#' @param a Some object.
#' @param ... Stuff.
#' @name my_generic
NULL

The function rd_s3_usage() creates the required \usage block as an (escaped) string, in the proper format for documenting S3 methods.

cat(rd_s3_usage("my_generic", "default", "numeric"))

#> \usage{
#> my_generic(x, \dots)
#> 
#> \method{my_generic}{default}(x, a = NULL, \dots)
#> 
#> \method{my_generic}{numeric}(x, a = 0, \dots)
#> }

In creating rd_s3_usage(), I've written helper functions that are more general than the task at hand requires, for these can then be reused (or adapted) in other situations where one wants to generate Rd blocks programmatically.

rd_dots <- function(x) gsub("\\.\\.\\.", "\\\\dots", x)

# Figure out calling signature of a function (given by name)
call_sig <- function(nm, cmd = nm, ...) {
  f <- get(nm, mode = "function", ...)
  sig <- deparse(call("function", formals(f), quote(expr = )))
  sig <- paste(trimws(sig, which = "left"), collapse = "")
  sig <- sub("^function", cmd, trimws(sig, which = "both"))

  rd_dots(sig)
}

# Make a vector of \usage{} entries for an S3 generic
s3_methods <- function(generic, ...) {
  classes <- list(...)
  rd_tmpl <- sprintf("\\\\method{%s}{%%s}", generic)
  cs_methods <- vapply(classes, function(cls) {
    method <- paste(generic, cls, sep = ".")
    rd_cmd <- sprintf(rd_tmpl, cls)
    call_sig(method, rd_cmd)
  }, character(1))

  c(call_sig(generic), cs_methods)
}

# Rd command markup
rd_markup <- function(cmd, join, sep) {
  force(join); force(sep)
  rd_cmd_opening <- paste0("\\", cmd, "{")

  function(x)
    paste(rd_cmd_opening, paste(x, collapse = join), "}", sep = sep)
}

rd_s3_usage <- function(...)
  rd_markup("usage", join = "\n\n", sep = "\n")(s3_methods(...))

Alas, running R CMD check still produces the dreaded Objects in \usage without \alias in documentation object 'my_generic' error. It seems that one must set method aliases to avoid it.

Semang answered 26/2, 2017 at 19:52 Comment(1)
Thanks, that looks cool. I'll give it a try when I get back to this issue in my project.Unipersonal

© 2022 - 2024 — McMap. All rights reserved.