Create an R package with dependencies - r

I'm trying to write my first R package. The functions in the package depend on the getURL() function from the RCurl package. I followed the tutorials on:
http://r-pkgs.had.co.nz/ and
http://hilaryparker.com/2014/04/29/writing-an-r-package-from-scratch/
I installed RTools, devtools and roxygen2 for writing the documentation and building the package.
The name of my package is "waterml". In my package I have the folder R with 3 files GetSites.R , GetVariables.R, GetValues.R. Each file has one function:
#' GetSites
#' #import XML
#' #importFrom RCurl getURL
#' This function gets the table of sites from the WaterML web service
#' #param server The URL of the web service ending with .asmx,
#' for example: http://worldwater.byu.edu/interactive/rushvalley/services/cuahsi_1_1.asmx
#' #keywords waterml
#' #export
#' #examples
#' GetSites("http://worldwater.byu.edu/interactive/rushvalley/services/cuahsi_1_1.asmx")
GetSites <- function(server) {
sites_url <- paste(server, "/GetSitesObject", sep="")
text <- RCurl::getURL(sites_url)
doc <- xmlRoot(xmlTreeParse(text, getDTD=FALSE, useInternalNodes = TRUE))
return(doc)
}
Now, I try to build the package:
library(devtools)
document()
The document() step completes without error. Now I run:
setwd("..")
install("waterml")
But I get the error:
* installing *source* package 'waterml' ...
** R
** preparing package for lazy loading
Error : object 'function' is not exported by 'namespace:RCurl'
ERROR: lazy loading failed for package 'waterml'
* removing 'C:/Program Files/R/R-3.1.2/library/waterml'
When I checked my NAMESPACE file, it contains some strange lines:
# Generated by roxygen2 (4.0.2.9000): do not edit by hand
export(GetSites)
export(GetValues)
export(GetVariables)
import(RCurl)
import(XML)
importFrom(RCurl,"function")
importFrom(RCurl,This)
importFrom(RCurl,WaterML)
importFrom(RCurl,data)
importFrom(RCurl,from)
importFrom(RCurl,getURL)
importFrom(RCurl,gets)
importFrom(RCurl,of)
importFrom(RCurl,series)
importFrom(RCurl,service)
importFrom(RCurl,sites)
importFrom(RCurl,table)
importFrom(RCurl,the)
importFrom(RCurl,time)
importFrom(RCurl,values)
importFrom(RCurl,variables)
importFrom(RCurl,web)
I think that the error is in the statement:
importFrom(RCurl, "function")
Any ideas what could be the problem? Am I using the #importFrom in the documentation of my function correctly?

Change:
#' GetSites
#' #import XML
#' #importFrom RCurl getURL
#' This function gets the table of sites from the WaterML web service
#' #param server The URL of the web service ending with .asmx,
To:
#' GetSites
#'
#' This function gets the table of sites from the WaterML web service
#'
#' #import XML
#' #importFrom RCurl getURL
#' #param server The URL of the web service ending with .asmx,
roxygen2 is reading the line following #importFrom and assuming each word is a function you want to import.

Related

Examples set using #examples fails devtools::check()

I am making an R package, and using roxygen2 to create documentation.
I have provided an example for one of the functions using:
#' #examples
#' \dontrun{
#' Train_model()
#' }
I have included an #export tag before this.
But, when I run devtools::check(). I get the following error:
Error in library("package1") :
there is no package called 'package1'
package1 is the name of the package I am creating.

R-package development: Documenting and exporting functions with non-syntactic names

I'm writing a package (called pac) where I have a function with a non-syntactic name (called +f). I'm unable to correctly document and export this function.
Here's a reproducible example:
library(devtools)
setwd("~/yourpath")
create("pac")
Save the following function definition along with its documentation in pac/R/+f.R
#' Add two objects
#'
#' #name `+f`
#'
#' #param x an object
#' #param y an object
#' #return A sum
#' #export
`+f`<- function(x, y) {x + y}
Then run
document()
load_all()
`+f`(2, 2)
Which produces the output
> document()
Updating pac documentation
Loading pac
Writing NAMESPACE
Writing tick-plus-f-tick.Rd
> load_all()
Loading pac
Warning message:
In setup_ns_exports(path, export_all, export_imports) :
Objects listed as exports, but not present in namespace: +f
> `+f`(2, 2)
Error in `+f`(2, 2) : could not find function "+f"
As you can see, the function `+f` is not exported correctly, even though the NAMESPACE-file does have the following line:
export(`+f`)
On the other hand, since the documentation file pac/man/tick-plus-f-tick.Rd exists, I am able to display it by calling ?"`+f`".
How can I define and document a function with a non-syntactic name in a package? What's going on in the above example? Why isn't the function available when pac is loaded, even though the NAMESPACE-file includes an export-statement?

What's the preferred means for defining an S3 method in an R package without introducing a dependency?

I have an R package (not currently on CRAN) which defines a couple of S3 methods of generic functions from other packages (specifically knitr::knit_print and huxtable::as_huxtable). However, they're not a key part of my package, so I'd prefer not to create a dependency on those packages when a user installs my package. Up until R 4.0.0, I exported the S3 methods without importing the generics. Using roxygen2, my #export directive was translated into an export() directive in NAMESPACE rather than S3method(). This worked fine in R versions < 4.0.0 because R looks in the global environment for a matching generic_function.class method first rather than relying on proper registration of the S3 method. However, as per this blog on developer.r-project.org, R no longer looks for non-registered S3 methods.
What is the best way round this? For now, I've added #importFrom directives to my roxygen2 blocks and have added both packages to the imports section of DESCRIPTION. However, as I understand things this will mean any user installing my package will then also have to install knitr and huxtable whether they want to or not.
Fortunately, for R >= 3.6.0, you don't even need the answer by caldwellst. From the blog entry you linked above:
Since R 3.6.0, S3method() directives in NAMESPACE can also be used to perform delayed S3 method registration. With S3method(PKG::GEN, CLS, FUN) function FUN will get registered as an S3 method for class CLS and generic GEN from package PKG only when the namespace of PKG is loaded. This can be employed to deal with situations where the method is not “immediately” needed, and having to pre-load the namespace of pkg (and all its strong dependencies) in order to perform immediate registration is considered too “costly”.
Additionally, this is also discussed in the docs for the other suggestion of vctrs::s3_register():
#' For R 3.5.0 and later, `s3_register()` is also useful when demonstrating
#' class creation in a vignette, since method lookup no longer always involves
#' the lexical scope. For R 3.6.0 and later, you can achieve a similar effect
#' by using "delayed method registration", i.e. placing the following in your
#' `NAMESPACE` file:
#'
#' ```
#' if (getRversion() >= "3.6.0") {
#' S3method(package::generic, class)
#' }
So, you would simply need to not use #importFrom and instead of #export, use #exportS3Method package::generic (See https://github.com/r-lib/roxygen2/issues/796 and https://github.com/r-lib/roxygen2/commit/843432ddc05bc2dabc9b5b22c1ae7de507a00508)
Illustration
So, to illustrate, we can make two very simple packages, foo and bar. The package foo just has a generic foo() function and default method:
library(devtools)
create_package("foo")
#' foo generic
#'
#' #param x An object
#' #param ... Arguments passed to or from other methods
#' #export
foo <- function(x, ...) {
UseMethod("foo", x)
}
#' foo default method
#'
#' #param x An object
#' #param ... Arguments passed to or from other methods
#' #export
foo.default <- function(x, ...) {
print("Called default method for foo.")
}
After document() and install()ing, we create bar:
create_package("bar")
which creates a bar method for foo():
#' bar method for foo
#'
#' #param x A bar object
#' #param ... Arguments passed to or from other methods
#'
#' #exportS3Method foo::foo
foo.bar <- function(x, ...) {
print("Called bar method for foo.")
}
Importantly, we must load the foo package before running document(), or #exportS3Method won't work. That is,
library(foo)
document()
But, if we do that, we get the following in the NAMESPACE for bar:
# Generated by roxygen2: do not edit by hand
S3method(foo::foo,bar)
We have to manually add foo to "Suggests" in DESCRIPTION.
Then if we uninstall foo, we can still install bar:
> remove.packages("foo")
Removing package from ‘/home/duckmayr/R/x86_64-pc-linux-gnu-library/4.0’
(as ‘lib’ is unspecified)
> install("bar")
✓ checking for file ‘/home/jb/bar/DESCRIPTION’ ...
─ preparing ‘bar’:
✓ checking DESCRIPTION meta-information ...
─ checking for LF line-endings in source and make files and shell scripts
─ checking for empty or unneeded directories
─ building ‘bar_0.0.0.9000.tar.gz’
Running /opt/R/4.0.0/lib/R/bin/R CMD INSTALL \
/tmp/Rtmp5Xgwqf/bar_0.0.0.9000.tar.gz --install-tests
* installing to library ‘/home/jb/R/x86_64-pc-linux-gnu-library/4.0’
* installing *source* package ‘bar’ ...
** using staged installation
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (bar)
The vctrs package provides a function called s3_register that dynamically registers methods for use in the .onLoad function. You can read more about its use here, for yourself you would want:
.onLoad <- function(...) {
if (requireNamespace("knitr", quietly = TRUE)) {
vctrs::s3_register("knitr::knit_print", "class_name")
}
if (requireNamespace("huxtable", quietly = TRUE)) {
vctrs::s3_register("huxtable::as_huxtable", "class_name")
}
}
The documentation is kind as well so you don't have to import vctrs:
To avoid taking a dependency on vctrs for this one function, please feel free to copy and paste the function source into your own package.

Cannot check my R package correctly

I have a own r package that works well. However when I check it, R delete me the export(predict.acb) on the NAMESPACE file.
In my .R file I have:
#' #export
acb<-function(Class,data,lambda,distance=NULL,prior=NULL,info.pred=NULL)
#' #export
predict.acb<-function(modelFit,newdata)
In my NAMESPACE I have
export(acb)
export(predict.acb)
After check I get
S3method(predict,acb)
export(acb)

Error use other library when I use Rstudio and Roxygen2 to build a package

To simplify the problem. I tried the following thing. My goal is to build a simple package which need another library.
I used RStudio and tried to create a new package, and checked the project option to "Generate document with Roxygen". And I get the following code:
#' Title just a test
#'
#' #return nothing
#' #export
#'
#' #examples
#' hello()
hello <- function() {
print("Hello, world!")
}
and I "check"ed it and "build and reload"ed it by the RStudio, all is OK.
Then I tried to add one line in the head of the code:
library("data.table")
#' Title just a test
#'
#' #return nothing
#' #export
#'
#' #examples
#' hello()
hello <- function() {
print("Hello, world!")
}
Then I failed amd get the following:
* checking whether package 'kanpu.temp' can be installed ... ERROR
Installation failed."
When I check the log, it says that:
* installing *source* package 'kanpu.temp' ...
** R
** preparing package for lazy loading
Error in library("data.table") : there is no package called 'data.table'
Error : unable to load R code in package 'kanpu.temp'
ERROR: lazy loading failed for package 'kanpu.temp'
* removing 'D:/onedrive/program/R/kanpu.temp.Rcheck/kanpu.temp'
I am sure that data.table is a existed package in my RStudio System. and also tried other package like "ggplot2", "plyr", and get the same result.
So how can I resolve this problem?
The envirement is:
Win7 64
RStudio 0.99.473
R 3.1.3 64
After checking the "Writing R Extensions", I know what's wrong with the code.
I should use "Import" or "Depends" in the "DESCRIPTION" file.
Looking at the error message, it seems that you do not have the ggplot2 package installed. This will cause an error when R reaches the line library(ggplot2).
The solution is to install that package:
install.packages("ggplot2")
However, you probably shouldn't be calling library in your packaged code. A package should make as few changes to the external environment as possible.
Instead, mark the package as required in your DESCRIPTION and make fully qualified function calls: SomePackage::someFunction().
See Hadley's pages for further information.

Resources