jacobbien / litr-project

Writing R Packages with Literate Programming

Home Page:https://jacobbien.github.io/litr-project/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Handling `package_name::` carefully

jacobbien opened this issue · comments

Suppose create-pkg.Rmd (that is defining a package pkg) defines two functions:

#' foo
foo <- function() {
  return(1)
}

#' bar
bar <- function() {
  pkg::foo()
}

This code will run ok once the package is installed; however, if we later call bar() in create-pkg.Rmd, there will be an error if pkg has never been installed (or worse, it will call an old version of pkg that was previously installed rather than the new foo() that has just been defined).

There was actually no good reason to have pkg:: in this context, so at first I thought we could just tell users not to use pkg:: when writing the generating .Rmd for pkg. However, I think there is at least one case where using pkg:: will be necessary.

Suppose we add a data set to our package:

usethis::use_data(dat)

And suppose we define a function that uses this data:

#' barbar
barbar <- function() {
  print(pkg::dat)
}

My understanding is that the package will not find dat (or it will think it's a global variable) unless we have the pkg:: there. However, now that means that if we try to call barbar() later in create-pkg.Rmd, we'll get an error (or it will use an out-of-date version of dat).

Behavior we want: When the hook send_to_package() is called, it should send off the code chunk to the package exactly as written. However, when the code is run for the sake of knitting create-pkg.Rmd, it should first remove any pkg::'s that appear.

I believe all we would need to do is add some code that modifies options$code directly after this line:

cat(paste(c(msg, "", options$code, ""), collapse = "\n"), file = file)

We could use something like str_remove_all(options$code, paste0(package_name, "::")).

I think this would be functionally correct, although a slightly strange aspect of it is that create-pkg.html would show code that differed from what was actually in the R package. To get this detail right might require some more finessing.

Actually, a problem with the approach suggested above is that if the user tries running the code blocks in the console (e.g. "Run All" in RStudio), then it will not work. For this reason, perhaps we should take the opposite approach and have users never use pkg:: and then have send_to_package() be responsible for adding pkg:: whenever the data set appears within a function. This actually seems much simpler than the above approach. The two complications will be

  • maintaining a list of the data sets that have been added to the package (to do so we can keep track of use_data() calls) and
  • defining a proper regex for this.

Note: After implementing the above, we should add to the make-an-r-package-with-data template an example where a function uses the data and then we can put a brief note calling attention to the fact that there is no rhasdata:: before the data set (even though this will be added to the package itself).