I am developing a package in Rstudio and I am trying to save object as internal to the package so that the user cannot see it. I make a default package project in Rstudio called "testpackage" and then execute:
library(devtools)
test.hidden.object <- 1:5
use_data(test.hidden.object,internal = T,overwrite = T)
Then I build the package, which saves it to my library. Then I restart Rstudio, and execute:
library(testpackage)
test.hidden.object
It prints out: [1] 1 2 3 4 5
The environment is empty, executing:
ls()
prints out "character(0)"
From what I understand, it is not possible to hide an object in a package from the user if the user knows the name of object, and I don't want to do that. But what worries me is that the autocomplete functionally is able to find these objects.
In both Rstudio and the R console, if I load the package and then type "test.hid" and then press TAB I can see the object "test.hidden.object" as an option. Should autocomplete be able to reveal internal objects? Am I building the package incorrectly?
To fix this problem I have so far updated R, Rstudio, devtools, and I have manually created the sysdata.rda file myself instead of using "use_data", but each time I am able to see the internal objects with autocomplete.
I think you are mistaken in your description. Autocompletion in RStudio and other R front ends will only show symbols that are visible in the current context. Your users can't make use of symbols that aren't exported, so autocompletion won't display them.
You may see hidden symbols while editing files in your own package, because your package code can see hidden symbols. But your users won't.
Edited to add: I've just followed your instructions more closely, and managed to duplicate what you saw. The problem is that by default the NAMESPACE file declares everything to be public, regardless of the setting for internal. This looks like a devtools misunderstanding or bug.
To fix it, manually edit your NAMESPACE file to make sure only public symbols are exported.
2nd edit: The docs for devtools::use_data have been updated on Github. They now say "If TRUE, stores all objects in a single R/sysdata.rda file. Objects in this file follow the usual export rules. Note that this means they will be exported if you are using the common exportPattern()
rule which exports all objects except for those that start with .."
Related
I’ve written some R functions and dropped them into a script file using RStudio. These are bits of code that I use over and over, so I’m wondering how I might most easily create an R package out of them (for my own private use).
I’ve read various “how to” guides online but they’re quite complicated. Can anyone suggest an “idiot’s guide” to doing this please?
I've been involved in creating R packages recently, so I can help you with that. Before proceeding to the steps to be followed, there are some pre-requisites, which include:
RStudio
devtools package (for most of the functions involved in creation of a package)
roxygen2 package (for roxygen documentation)
In case you don't have the aforementioned packages, you can install them with these commands respectively:
install.packages("devtools")
install.packages("roxygen2")
Steps:
(1) Import devtools in RStudio by using library(devtools).
(devtools is a core package that makes creating R packages easier with its tools)
(2) Create your package by using:
create_package("~/directory/package_name") for a custom directory.
or
create_package("package_name") if you want your package to be created in current workspace directory.
(3) Soon after you execute this function, it will open a new RStudio session. You will observe that in the old session some lines will be auto-generated which basically tells R to create a new package with required components in the specified directory.
After this, we are done with this old instance of RStudio. We will continue our work on the new RStudio session window.
By far the package creation part is already over (yes, that simple) however, a package isn't directly functionable just by its creation plus the fact that you need to include a function in it requires some additional aspects of a package such as its documentation (where the function's title, parameters, return types, examples etc as mentioned using #param, #return etc - you would be familiar if you see roxygen documentation like in some github repositories) and R CMD checks to get it working.
I'll get to that in the subsequent steps, but just in case you want to verify that your package is created, you can look at:
The top right corner of the new RStudio session, where you can see the package name that you created.
The console, where you will see that R created a new directory/folder in the path that we specified in create_package() function.
The files panel of RStudio session, where you'll notice a bunch of new files and directories within your directory.
(4) As you mentioned in your words, you drop your functions in a script file - hence you will need to create the script first, which can be done using:
use_r("function_name")
A new R script will pop up in your working session, ready to be used.
Now go ahead and write your function(s) in it.
(5) After your done, you need to load the function(s) you have written for your package. This is accomplished by using the devtools::load_all() function.
When you execute load_all() in the console, you'll get to know that the functions have been loaded into your package when you'll see Loading package_name displayed in console.
You can try calling your functions after that in the console to verify that they work as a part of the package.
(6) Now that your function has been written and loaded into your package, it is time to move onto checks. It is a good practice to check the whole package as we make changes to our package. The function devtools::check() offers an easy way to do this.
Try executing check() in the console, it will go through a number of procedures checking your package for warnings/errors and give details for the same as messages on the screen (pertaining to what are the errors/warnings/notes). The R CMD check results at the end will contain the vital logs for you to see what are the errors and warnings you got along with their frequency.
If the functions in your package are written well, (with additional package dependencies taken care of) it will give you two warnings upon execution of check:
The first warning will be regarding the license that your package uses, which is not specified for a new pacakge.
The second should be the one for documentation, warning us that our code is not documented.
To resolve the first issue which is the license, use the use_mit_license("license_holder_name") command (or any other license which suits your package - but then for private use as you mentioned, it doesn't really matter what you specify if only your going to use it or not its to be distributed) with your name as in place of license_holder_name or anything which suits a license name.
This will add the license field in the .DESCRIPTION file (in your files panel) plus create additional files adding the license information.
Also you'll need to edit the .DESCRIPTION file, which have self-explanatory fields to fill-in or edit. Here is an example of how you can have it:
Package: Your_package_name
Title: Give a brief title
Version: 1.0.0.0
Authors#R:
person(given = "Your_first_name",
family = "Your_surname/family_name",
role = c("package_creator", "author"),
email = "youremailaddress#gmail.com",
comment = c(ORCID = "YOUR-ORCID-ID"))
Description: Give a brief description considering your package functionality.
License: will be updated with whatever license you provide, the above step will take care of this line.
Encoding: UTF-8
LazyData: true
To resolve the documentation warning, you'll need to document your function using roxygen documentation. An example:
#' #param a parameter one
#' #param b parameter two
#' #return sum of a and b
#' #export
#'
#' #examples
#' yourfunction(1,2)
yourfunction <- function(a,b)
{
sum <- a+b
return(sum)
}
Follow the roxygen syntax and add attributes as you desire, some may be optional such as #title for specifying title, while others such as #import are required (must) if your importing from other packages other than base R.
After your done documenting your function(s) using the Roxygen skeleton, we can tell our package that we have documented our functions by running devtools::document(). After you execute the document() command, perform check() again to see if you get any warnings. If you don't, then that means you're good to go. (you won't if you follow the steps)
Lastly, you'll need to install the package, for it to be accessible by R. Simply use the install() command (yes the same one you used at the beginning, except you don't need to specify the package here like install("package") since you are currently working in an instance where the package is loaded and is ready to be deployed/installed) and you'll see after a few lines of installation a statement like "Done (package_name)", which indicates the installation of our package is complete.
Now you can try your function by first importing your package using library("package_name") and then calling your desired function from the package. Thats it, congrats you did it!
I've tried to include the procedure in a lucid way (the way I create my R packages), but if you have any doubts feel free to ask.
Update
Issue resolved
Update, still not working
Tried the following in R file
(1) deleted both library(...) packages
(2) Added #import jpeg before ShowPalettePhoto() and #import tidyverse before RanglaPunjab() so roxygen automatically adds to NAMESPACE.
After running devtools::document(), ran devtools::use_package("jpeg") and devtools::use_package("tidyverse") to automatically add to DESCRIPTION.
Unfortunately, even in testing, I cannot get JPEG photo.
Here is GitHub repository, https://github.com/ArtieLadie/RanglaPunjab
I created R package according to this tutorial
It worked and I was able to execute all commands, including a function to display photo in another directory.
I uploaded to my GitHub account. Anyone can install package in R environment with install_github("ArtieLadie/RanglaPunjab")
I am able to run functions by adding RanglaPunjab:: in front of it, i.e.
RanglaPunjab::PaintPalette("Jutti")
?RanglaPunjab::MergePalette
However, when I try to run ?RanglaPunjab::ShowPalettePhoto("Teej") I get
Error in readJPEG(x, native = TRUE) : could not find function "readJPEG"
Before creating the package I added function to set working directory to file location, but it was creating errors when I ran install("RanglaPunjab"), i.e. "Cannot execute"
Here are the exact commands I had, which I had to delete from code
library(rstudioapi)
current_path <- getActiveDocumentContext()$path
setwd(dirname(current_path ))
Please help
Your dependencies are not handled correctly. Here you explicitly load packages with library(...). That is not how one does that in an R package. You should add your dependencies to the Imports: section of the DESCRIPTION file and use the package::function() syntax when calling the function. c.f. http://r-pkgs.had.co.nz/description.html#dependencies.
In addition, if you want the images to be installed with your package, you should place them for example in inst/pics. You can then get the path to these files with
system.file("pics", <file-name>, package = "RanglaPunjab")
When I develop a R package I endup doing the following thing:
load the latest build
use it
realize there is a bug in say function 'f_bug'
try to debug
I would be easy if I could just 're-source' f_bug and that the newly sourced version would be chosen (I'd rebuild the package clean latter).
But I cannot do that, it looks like the package::f_bug is always "chosen" by default when called within another package function.
Can I do such a thing ?
You can't use RStudio's convenient graphical breakpoints, but you can do the same thing using trace:
trace(package::f_bug, browser, at = insertion_point)
Here insertion_point refers not to line numbers but to a vector of substeps. From ?trace:
look at ‘as.list(body(f))’ to get the numbers
associated with the steps in function ‘f’.)
Another option might be to use utils::setBreakpoint which takes a file name and line number as arguments. See the help file for details.
I'm attempting to generate reference classes within an R package on the fly, and it's proving to be fairly difficult. Here are the approaches I've taken and problems I've run into:
I'm creating a package in which I hope to be able to dynamically read in a schema and automatically generate an associated reference class (think SOAP). Of course, this means I won't be able to define my reference classes before-hand in the package sources.
I initially attempted to create a new class using a simple:
myClass <- setRefClass("NewClassName", fields=list(fieldA="character"))
which, of course, works fine when executed interactively, but when included in the package sources, I get a locked binding error. From my reading, it looks like this occurs because when running interactively, the class information is being stored in the global environment, which is not locked, while my package's base environment is locked.
I then found a thread that suggested using something to the effect of:
myClass <- setRefClass("NewClassName", fields=list(fieldA="character"), where=globalenv())
This actually crashed R/Studio when I tried to build the package, so I don't have a log of the error it generated, unfortunately, but it certainly didn't work.
Next I tried creating a new environment within my package which I could use to store these reference classes. So I added a .classEnv <- new.env() line in my package sources (not inside of any function) and then attempted to use this class when creating a new reference class:
myClass <- setRefClass("NewClassName", fields=list(fieldA="character"), where=.classEnv)
This actually seemed to work OK, but generates the following warning:
> myClass <- setRefClass("NewClassName", where=.classEnv)
Warning message:
In getPackageName(where) :
Created a package name, ‘2013-04-23 10:19:14’, when none found
So, for some reason, methods::getPackageName() isn't able to pick up which package my new environment is in?
Is there a way to create my new environment differently so that getPackageName() can properly recognize the package? Can I add some feature which allows me to help getPackageName() detect the package? Will this even work if I can deal with the warning, or am I misusing reference classes by trying to create them dynamically?
To get the conversation going, I found that getpackageName stores the package name in a hidden .packageName variable in the specified environment.
So you can actually get around the warning with
assign(".packageName", "MyPkg", envir=.classEnv)
myClass <- setRefClass("NewClassName", fields=classFields, where=.classEnv)
which resolves the warning, but the documentation says not to trust the .packageName variable indefinitely, and I still feel like I'm hacking this in and may be misunderstanding something important about reference classes and their relationship to environments.
Full details from documentation:
Package names are normally installed during loading of the package, by the INSTALL script or by the library function. (Currently, the name is stored as the object .packageName but don't trust this for the future.)
Edit:
After reading a little further, the setPackageName method may be a more reliable way to set the package name for the environment. Per the docs:
setPackageName can be used to establish a package name in an environment that would otherwise not have one. This allows you to create classes and/or methods in an arbitrary environment, but it is usually preferable to create packages by the standard R programming tools (package.skeleton, etc.)
So it looks like one valid solution would be the following:
setPackageName("MyPkg", .classEnv)
myClass <- setRefClass("NewClassName", fields=classFields, where=.classEnv)
That eliminates the warning message and doesn't rely on anything that's documented as unstable. I'm still not clear why it's necessary, but...
My paths and files are as follows ...
E:R/R-2.15.1/library/Rcmdr/
E:R/R-2.15.1/library/RcmdrPlugin.Package/
E:R/R-2.15.1/MyLibrary/RcmdrPlugin.Package.zip
E:R/R-2.15.1/MyLibrary/RcmdrPlugin.Package/
where, in the name RcmdrPlugin.Package, I've used the word 'Package' to represent the name of the actual package being used.
The installation is as described above because (i) I'm not an expert at installing packages, (ii) I couldn't do a direct install from Cran because I wanted to put the package onto a USB stick; and, (iii) at work the Cran server is blocked (sic).
When I start the package from the GUI the Cmdr opens once and quickly closes (I don't know if this is relevant or normal) and opens again. Once open, I can operate the package via the Cmdr interface. It's a very nice package, everything works really well until I want to save the work. Then I get the following error,
Error in obj[i] : object of type 'closure' is not subsettable
I've been in contact with the people who developed (and are still developing) the package and they cannot reproduce the bug.
I strongly suspect that the problem lies in my 'crappy' install and file configuration, rather than with the package.
Can anyone please help me by suggesting how I would undo what I've done and do it properly in view of the constraints list above?
I appreciate that I can use Remove to get rid of the package but I don't want to start tinkering with something without having a greater understanding of what I'm doing.
Lastly, note that is error has been discussed a number of times on this list but not within this particular context.
I've managed to get it to work by following the instructions from here stackoverflow.com/questions/12820189/… where I set repos=NULL after I'd put the package into /MyLibrary/