Документирование классов и методов R6 в пакете R в RStudio
Я борюсь с документацией класса R6 и его методов. Моя цель - получить автозаполнение в RStudio для методов. На данный момент я получаю только имя метода, но не могу получить справочную информацию, которую обычно получаю roxygen2
документирование функции с параметрами и т. д.
Может ли кто-нибудь помочь мне в этом?
На данный момент это мой класс:
#' @importFrom R6 R6Class
MQParameters <- R6::R6Class(
'MQParameters',
public=list(
initialize=function(file_path=NA) {
private$location <- file_path
mq_parameters <- read.delim(file_path, stringsAsFactors=FALSE)
mq_parameters <-
setNames(mq_parameters$Value, mq_parameters$Parameter)
private$mq_version <- unname(mq_parameters['Version'])
private$fasta_file <-
gsub('\\\\', '/', strsplit(mq_parameters['Fasta file'], ';')[[1]])
},
# this method returns the version
getVersion=function() {
private$mq_version
},
# this methods returns the fastafile.
# @param new_param it is possible to rewrite the basedir.
getFastaFile=function(new_basedir=NA) {
if(is.na(new_basedir)) {
private$fasta_file
} else {
file.path(new_basedir, basename(private$fasta_file))
}
}
),
private=list(
location=NULL,
mq_version=NULL,
fasta_file=NULL
)
)
Если вы заинтересованы в тестировании этого класса, вот небольшой воспроизводимый пример:
df <- data.frame(Parameter=c('Version', 'Fasta file'),
Value=c('1.5.2.8','c:\\a\\b.fasta'))
write.table(df, 'jnk.txt', sep='\t', row.names=F)
p <- MQParameters$new('jnk.txt')
p$getVersion()
# [1] "1.5.2.8"
p$getFastaFile()
# [1] "c:/a/b.fasta"
p$getFastaFile(new_basedir='.')
# [1] "./b.fasta"
Я не знаю, как документировать параметры, потому что параметры на самом деле принадлежат создателю, а не классу. А как насчет параметров других методов внутри функции?
Каков предпочтительный способ документировать класс с помощью его методов?
Я хотел бы получить "нормальную" функциональность от RStudio, как удар F1
чтобы попасть прямо на страницу справки.
Любой совет очень приветствуется.
Поиская в Интернете, я уже видел некоторые сообщения на github по этой теме, но им уже более 1 года, и я надеюсь, что есть какие-то хорошие новости или, по крайней мере, какая-то хорошая практика.
ОБНОВИТЬ:
Благодаря ответу mikeck
Теперь у меня есть хорошая документация для класса и его методов. Но мне все еще не хватает возможности получить подсказку о функции / методе и ее аргументах, как на этом скриншоте для обычной функции:
Мне интересно, могу ли я как-то зарегистрировать свою функцию вручную, но так как она не имеет определенного имени (она всегда связана с переменной objectname, которую вы используете для объекта OBJECTNAME$methodeCall()
Я не знаю, как это сделать.
2 ответа
Насколько я понимаю, проще всего задокументировать NULL
объект с тем же @name
как ваш класс, так как это обеспечивает максимальную гибкость. Я использую класс R6 в одном из моих пакетов; Вы можете посмотреть содержание кислорода здесь. Я включил небольшой пример ниже:
#' Python Environment
#'
#' The Python Environment Class. Provides an interface to a Python process.
#'
#'
#' @section Usage:
#' \preformatted{py = PythonEnv$new(port, path)
#'
#' py$start()
#'
#' py$running
#'
#' py$exec(..., file = NULL)
#' py$stop(force = FALSE)
#'
#' }
#'
#' @section Arguments:
#' \code{port} The port to use for communication with Python.
#'
#' \code{path} The path to the Python executable.
#'
#' \code{...} Commands to run or named variables to set in the Python process.
#'
#' \code{file} File containing Python code to execute.
#'
#' \code{force} If \code{TRUE}, force the Python process to terminate
#' using a sytem call.
#'
#' @section Methods:
#' \code{$new()} Initialize a Python interface. The Python process is not
#' started automatically.
#'
#' \code{$start()} Start the Python process. The Python process runs
#' asynchronously.
#'
#' \code{$running} Check if the Python process is running.
#'
#' \code{$exec()} Execute the specified Python
#' commands and invisibly return printed Python output (if any).
#' Alternatively, the \code{file} argument can be used to specify
#' a file containing Python code. Note that there will be no return
#' value unless an explicit Python \code{print} statement is executed.
#'
#' \code{$stop()} Stop the Python process by sending a request to the
#' Python process. If \code{force = TRUE}, the process will be
#' terminated using a system call instead.
#'
#' @name PythonEnv
#' @examples
#' pypath = Sys.which('python')
#' if(nchar(pypath) > 0) {
#' py = PythonEnv$new(path = pypath, port = 6011)
#' py$start()
#' py$running
#' py$stop(force = TRUE)
#' } else
#' message("No Python distribution found!")
NULL
#' @export
PythonEnv = R6::R6Class("PythonEnv", cloneable = FALSE,
# actual class definition...
Есть другие альтернативные (но похожие) реализации; этот пример использует @docType class
что может подойти вам лучше:
#' Class providing object with methods for communication with lightning-viz server
#'
#' @docType class
#' @importFrom R6 R6Class
#' @importFrom RCurl postForm
#' @importFrom RJSONIO fromJSON toJSON
#' @importFrom httr POST
#' @export
#' @keywords data
#' @return Object of \code{\link{R6Class}} with methods for communication with lightning-viz server.
#' @format \code{\link{R6Class}} object.
#' @examples
#' Lightning$new("http://localhost:3000/")
#' Lightning$new("http://your-lightning.herokuapp.com/")
#' @field serveraddress Stores address of your lightning server.
#' @field sessionid Stores id of your current session on the server.
#' @field url Stores url of the last visualization created by this object.
#' @field autoopen Checks if the server is automatically opening the visualizations.
#' @field notebook Checks if the server is in the jupyter notebook mode.
#' #' @section Methods:
#' \describe{
#' \item{Documentation}{For full documentation of each method go to https://github.com/lightning-viz/lightining-r/}
#' \item{\code{new(serveraddress)}}{This method is used to create object of this class with \code{serveraddress} as address of the server object is connecting to.}
#'
#' \item{\code{sethost(serveraddress)}}{This method changes server that you are contacting with to \code{serveraddress}.}
#' \item{\code{createsession(sessionname = "")}}{This method creates new session on the server with optionally given name in \code{sessionname}.}
#' \item{\code{usesession(sessionid)}}{This method changes currently used session on the server to the one with id given in \code{sessionid} parameter.}
#' \item{\code{openviz(vizid = NA)}}{This method by default opens most recently created by this object visualization. If \code{vizid} parameter is given, it opens a visualization with given id instead.}
#' \item{\code{enableautoopening()}}{This method enables auto opening of every visualisation that you create since that moment. Disabled by default.}
#' \item{\code{disableautoopening()}}{This method disables auto opening of every visualisation that you create since that moment. Disabled by default.}
#' \item{\code{line(series, index = NA, color = NA, label = NA, size = NA, xaxis = NA, yaxis = NA, logScaleX = "false", logScaleY = "false")}}{This method creates a line visualization for vector/matrix with each row representing a line, given in \code{series}.}
#' \item{\code{scatter(x, y, color = NA, label = NA, size = NA, alpha = NA, xaxis = NA, yaxis = NA)}}{This method creates a scatterplot for points with coordinates given in vectors \code{x, y}.}
#' \item{\code{linestacked(series, color = NA, label = NA, size = NA)}}{This method creates a plot of multiple lines given in matrix \code{series}, with an ability to hide and show every one of them.}
#' \item{\code{force(matrix, color = NA, label = NA, size = NA)}}{This method creates a force plot for matrix given in \code{matrix}.}
#' \item{\code{graph(x, y, matrix, color = NA, label = NA, size = NA)}}{This method creates a graph of points with coordinates given in \code{x, y} vectors, with connection given in \code{matrix} connectivity matrix.}
#' \item{\code{map(regions, weights, colormap)}}{This method creates a world (or USA) map, marking regions given as a vector of abbreviations (3-char for countries, 2-char for states) in \code{regions} with weights given in \code{weights} vector and with \code{colormap} color (string from colorbrewer).}
#' \item{\code{graphbundled(x, y, matrix, color = NA, label = NA, size = NA)}}{This method creates a bundled graph of points with coordinates given in \code{x, y} vectors, with connection given in \code{matrix} connectivity matrix. Lines on this graph are stacked a bit more than in the \code{graph} function.}
#' \item{\code{matrix(matrix, colormap)}}{This method creates a visualization of matrix given in \code{matrix} parameter, with its contents used as weights for the colormap given in \code{colormap} (string from colorbrewer).}
#' \item{\code{adjacency(matrix, label = NA)}}{This method creates a visualization for adjacency matrix given in \code{matrix} parameter.}
#' \item{\code{scatterline(x, y, t, color = NA, label = NA, size = NA)}}{This method creates a scatterplot for coordinates in vectors \code{x, y} and assignes a line plot to every point on that plot. Each line is given as a row in \code{t} matrix.}
#' \item{\code{scatter3(x, y, z, color = NA, label = NA, size = NA, alpha = NA)}}{This method creates a 3D scatterplot for coordinates given in vectors \code{x, y, z}.}
#' \item{\code{image(imgpath)}}{This method uploads image from file \code{imgpath} to the server and creates a visualisation of it.}
#' \item{\code{gallery(imgpathvector)}}{This method uploads images from vector of file paths \code{imgpathvector} to the server and creates a gallery of these images.}}
Lightning <- R6Class("Lightning",
...
)
РЕДАКТИРОВАТЬ
Если вы ищете способ отображения всплывающих подсказок RStudio при использовании метода класса... к сожалению, я не думаю, что вы найдете решение, которое не требует кодирования ваших классов таким образом, чтобы исключить удобство и функциональность классов R6.
@ f-privé предоставил ответ, который будет делать то, что вы хотите - просто распространите эту логику на ВСЕ методы. Например, myclass$my_method
вместо этого доступен
my_method = function(r6obj) {
r6obj$my_method()
}
obj$my_method()
my_method(obj) # equivalent
Другими словами, вам нужно будет создать оболочку для каждого метода. Это, очевидно, менее удобно для программирования, чем просто использование obj$my_method()
и, вероятно, убивает полезность использования класса R6 в первую очередь.
Проблема здесь действительно RStudio. В среде IDE нет хорошего способа определения классов R6 путем анализа кода, и она не может различить методы определенного класса и элементы списка или среды. Кроме того, RStudio не может предоставить справку по произвольным функциям, таким как:
na.omit() # tooltip shows up when cursor is within the parentheses
foo = na.omit
foo() # no tooltip
что довольно аналогично вызову методов конкретного объекта R6.
Я думаю, что R люди не хотят использовать $new(...)
чтобы получить экземпляр нового класса. Они предпочитают иметь функцию с тем же именем класса, чтобы создать его экземпляр.
Итак, что вы можете сделать, это переименовать ваш R6ClassGenerator MQParameters_R6Class
и создать другую функцию
MQParameters <- function(file_path = NA) {
MQParameters_R6Class$new(file_path)
}
Затем задокументируйте эту функцию как любую другую функцию, и вы получите "маленькое желтое окно, показывающее вызов функции с ее аргументами" из RStudio. И счастливые пользователи R.