Automating iface
specifications
Adopting interfacer
for package functions can go hand in
hand with developing test data for the project. In this scenario a
function that relies on a specific dataframe format, can be defined
using the test data as a prototype to help generate the
iface
specification.
This is the role of iclip
and use_iface
.
Suppose we wish to develop a function that supports datasets in the same
format as mtcars
we can use the mtcars
dataset
as a prototype by calling iclip(mtcars)
. This writes a
iface
specification to the clipboard. Pasting it gives
us:
i_mtcars = interfacer::iface(
mpg = numeric ~ "the mpg column",
cyl = numeric ~ "the cyl column",
disp = numeric ~ "the disp column",
hp = numeric ~ "the hp column",
drat = numeric ~ "the drat column",
wt = numeric ~ "the wt column",
qsec = numeric ~ "the qsec column",
vs = numeric ~ "the vs column",
am = numeric ~ "the am column",
gear = numeric ~ "the gear column",
carb = numeric ~ "the carb column",
.groups = NULL
)
If we instead called use_iface(mtcars)
this definition
would be written to the file R/interfaces.R
(or the
definition updated if it is already present). iface
specifications can be anywhere in the package hierarchy but it does make
some sense to keep them all in one file. Interface specifications do not
need to be exported from a package to function (although they can
be).
In both cases as the developer you will want to update the default
column description, if the use_iface
function was used,
care must be taken to ensure changes you make will not be over written
if use_iface
is called again. This is a question of
removing the relevant comment in R/interfaces.R
Dataframe documentation
When using usethis::use_data
to embed data in a package
there is inevitably a reminder to document your data. When you are
embedding a dataframe interfacer
can inspect your dataframe
and generate a template data documentation into R/data.R
at
the same this as embedding the data.
This is triggered with a call to, for example,
interfacer::use_dataframe(mtcars)
which will create an
entry in R/data.R
for your dataframe documentation.
This function uses the interfacer
framework to generate
the documentation but does not need it afterwards.
roxygen2
documentation
@iparam <name> <description>
tags can be
used in the roxygen2
documentation of an
interfacer
enabled function. This enables
devtools::document()
to automatically write the
documentation for dataframe parameters. It may require that you call
library(interfacer)
before running
devtools::document()
. In this example, the
@iparam
tag will be expanded to include the documentation
of the expected input as defined in the iface
specification
of the df
parameter:
#' A function
#'
#' @iparam df An input dataframe
#' @return ... something ...
test_function = function(df = interfacer::iface(col1 = integer ~ "An integer value")) {
df = interfacer::ivalidate(df)
# ... main function body ...
}
The @iparam
tag picks the iface
specification from the current function and parameter. A more flexible
alternative is provided by idocument()
which allows you to
specify the function and parameter you wish to document. This is useful
if documenting a generic function that may dispatch to multiple
functions based on the dataframe structures.
# This may be defined in the file R/interfaces.R
i_type1 = interfacer::iface(col1 = integer ~ "An integer value")
i_type2 = interfacer::iface(col1 = date ~ "A date value")
#' A mulitple dispatch function
#'
#' @param df An input dataframe conforming to one of:
#' `r interfacer::idocument(test_function.type1, df)`
#' or
#' `r interfacer::idocument(test_function.type2, df)`
#'
#' @return ... something ...
test_function = function(df) {
interfacer::idispatch(df,
test_function.type1 = i_type1,
test_function.type2 = i_type2
)
}
test_function.type1 = function(df = i_type1) {
# ... deal with integer input ...
}
test_function.type1 = function(df = i_type2) {
# ... deal with date input ...
}
If, as in the previous example, the iface
definitions
are defined as package local variables it is also possible to refer
directly to these variables in the documentation where they will be
expanded to their definition. This is also the recommended way to
document return values:
# This may be defined in the file R/interfaces.R
i_input_type = interfacer::iface(col1 = integer ~ "An integer value")
i_return_type = interfacer::iface(output = date ~ "A date value")
#' An example function
#'
#' @param df An input dataframe conforming to:
#' `r i_input_type`
#'
#' @return a dataframe of the following format:
#' `r i_return_type`
test_function = function(df = i_input_type) {
df = interfacer::ivalidate(df)
# ... main function body ...
interfacer::ireturn( ...output... , i_return_type)
}