knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
install.packages(c("devtools", "roxygen2", "testthat", "knitr")) install.packages("digest") install.packages("rcmdcheck") devtools::install_github("hadley/devtools") library("devtools") x <- has_devel() x
*In the book it says has_devel()
will return a bunch of diagnostics and then TRUE
, but it now seems to return the value silently---invisibly. *
Says it doesn't matter if you start a package from Rstudio or using devtools::create()
even though Robert Flight says a rstudio created project won't pass the CRAn tests - although he did say that in 2014.
At this point I start a package as a new project, from Rstudio, also initialising a git
repo and a packrat
file. Best see how this works in practice.
After I do the initial commit I realise I obviously don't have a github repo yet, so I start a new one and then use:
git remote add origin git@github.com:majazaloznik/pkgs.had.git
to add the repository.
Then I also start this journal as a vingette, because what else am I gonna write it as?
devtools::use_vignette("journal")
This is in the vignettes chapter although it is deprecated. You're supposed to use usethis::use_vignette()
. Note that the default output is html, which I have for the time being changed to pff_document
, but not sure how this works when you build your package.
A source package is the development version of a package that lived on my conmputer: the directory with all the components e.g. R/
, DESCRIPTION
etc.
Is compressed to a single file---by linux convention tar.gz
. If you decompress a bundle it will look almost like a source package. You create a bundle using devtools::build()
, which will bundle everyhting but what is in the Rbuildignore
file and temporary files like compilation files in src/
. Bundles also have the vignettes built to html and pdf.
Rbuildignore
lets you have additional files and folders in the source package that are not included in the bundle e.g. data you generated: you want the data in the source package but doens't need to be distributed.
Use Perl-compatible regular expresions to exclude files. (e.g. notes
will also exclude end.notes.R
, so you have to say ^notes$
to be clear that the name begins ^
and ends $
with notes.
You can use devtools::use_build_ignore()
to have it do the regexing for you.
To distribute to someone without development tools, you need a binary. This is also a single file, but when uncompressed it is slightly different to the source package:
R/
directory but .RData
files instead, three of them, that are basically the functions loaded into R and saved. Meta/
directory with a number of Rds
files, which contains metadata about the package. This is also for faster loading. html/
directory with the html help filessrc/
directory there will now be a libs/
directory that contains the results of compiling 32 bit and 64 bit code."inst/
directory are moved to the top level. Binary packages are platform specific.
Use devtools::build(binary = TRUE)
to build a binary---although it doesnt' say how you bild one specific to each platform? Oh, seems like it's current platform only?
Is a binary that has been decompressed into a package library. This can be done in several ways. e/g/:
install.packages()
To use a package you must load it into memory. There is an important disticntion between loading and loading and attaching an installed package.:
devtools::install()
---loads the devtools packagelibrary(devtools); install()
loads and attaches devtools. This will apparently be explained later, but for now, library is not useful when you're developing the package, because you have to install it first.
A library is simply a directory containing packages. Use .libPaths()
to see what you've got active
All code goes into R\
.
(Re)load your code with Ctrl+Shift+L. Then 'explore the code in the console' I don't get this, what is that supposed to mean?. OK, so this is the same as devtools::load_all()
, whic builds the package from source and installs it and loads it/attaches. so explore the code means you can access the updated functions, in the console. Use Ctrl+Shift+L, because it also saves the files.
Putting them all in one file or each in their own are both bad!? Hmm, I'm def in the latter camp, unless there is a good reason to merge more together. OK, rule of thumb: it must be easy to remember where each function lives. You can't use subdirectories inside R\
.
Looking for functions within a file:
F2
will take you to the definition+ .
will open a fucniton search box.Then get back to where you were with Ctrl+F9
formatR
and lintr
are an automatic and static approach to keeping clean code approx in line with the Rstudio guidelines.
Functions and variiables, all lower case, with underscore, not fullstop---reserved for S3 methods. Variable names should be nouns and funcion names should be verbs.
Around operators except :
, before (
except in funciton call, after ,
not before
Open {
never on own line, always followed by newline; closed }
always on new line ugh except before else
Explain the why, not the what!
Code in packages---as opposed to scripts---is run when it is built, so it should only build objects, mainly functions. Not a clear sentence, really Aha, he's trying to say not to write top-level code, because it won't get executed when you load it (see more below).
Anticipate unimagined applications of your code by other people.
So in a package the code is executed when you build it, but not when you load it. So top-level code (e.g. library(ggplot)
won't execute when you install a package, installing a package will only load the objects into the environment. So this sort of top level code has to be moved into the functions themselves if you want it available---but don't do that with libraries, do that in the DESCRIPTION
file as we'll see later.
Don't change the landscape---it will not be the same for other people and will make things more difficult to understand. library()
, source()
, setwd()
, options()
are all landscape changing functions and you probably don't want to use them.
Although I'm again not particulalry happy with the explanation here, so you should avoid things that change the behaviour of other functions after you're done, so you should always revert them back to the way they were using on.exit()
.
Also, be careful not to make assumptions about the user's landscape¬
Sometimes you do ened some initial setup (what would be equivalent to top-level code in a script file) in a package. Use onLoad()
and onAttach()
for this (mainly the first one unless directed otherwise). Uses:
.onAttach <- function(libname, pkgname) { packageStartupMessage("Welcome to my package") }
.onLoad <- function(libname, pkgname) { op <- options() op.devtools <- list( devtools.path = "~/R-dev", devtools.install.args = "", devtools.name = "Your name goes here", devtools.desc.author = "First Last <first.last@example.com> [aut, cre]", devtools.desc.license = "What license is it under?", devtools.desc.suggests = NULL, devtools.desc = list() ) toset <- !(names(op.devtools) %in% names(op)) if(any(toset)) options(op.devtools[toset]) invisible() }
This is again not clearly explained at all. To avoid conflicts with other packages you prefix the options with the package name, in this case devtools
, and also you test if the user has already set the options themself before you override them. After you do this you can use getOption("devtools.name")
for example, to access the value. Oh, invisible()
is used instead of return to mean that you can assign the value, but it won't be printed if you don't. But not sure why it is necessary here?
to talk to other programming languages like e.g. calling rJava::.jpackage()
to register vignette engines with tools::vignetteEngine()
whatever the hell that means.
These last two bullet points are not clear at all tbh. The last one must be based on this section in the manual which unfotunately isn't massively more helpful.
Hopefully there will be more info on .onLoad()
, but yeah, you should consider using onUnload()
to clean up as well, and these files are by convention saved in a file called zzz.R
Also---apart from the mention in 'Object names' that fullstops should be reserved for S3 objets---there is no explanaiton for the periods in names, especially names starting with them. Which is odd, since they are useful in package building.
Another side-effect that you do want to ensure S4 clases are defined in the right order, with methods and generics, so the R\
folder needs to be sourced in a specific order, which you do through the DESCRIPTION
file as well, we'll see later.
The DESCRIPTION
file is the defining feature of a package, if it's in the folder, R considers it a package. Minimal bare-bones is provided when you create it using devtools
or Rstudio.
Imports: dplyr Suggests: ggvis vcd
Comma separated lists of packages. Imports
means the packages are necessary for your package to work. If not present, then devtools_load_all()
will install them, however they will not be attached along with your package! Best practice is to use ::
to refer to external fucntions, also makes it more readable.
Suggests
means the packages are not required, they might be useful for example datasets to run tests, build vignettes etc.They will not be automatically installed, so if you want to use stuff from there you need to use requireNamespace(x, quetly - TRUE)
to check if it is available before you try to use it:
# Option 1: exit if the package isn't available my_fun <- function(a, b) { if (!requireNamespace("pkg", quietly = TRUE)) { stop("Package \"pkg\" needed for this function to work. Please install it.", call. = FALSE) } } # Use alternative if package isn't available my_fun <- function(a, b) { if (requireNamespace("pkg", quietly = TRUE)) { pkg::f() } else { g() } }
usethis::use_package("dplyr")
to add a package dependency automatically the devtools::
version is deprecated. [^1]
[^1]: this issue here explains that the deprecation is a temp warning as the devtools
package is on it's way to becoming a wrapper package, like tidyveres, so while these funcitons will live in usethis
, they will automatically be loaded with devtools, so it won't change anything from the user's perspective.
You should specify the version of the package required:
Imports:
dplyr (>= 0.2),
ggbis(>= 0.3.1.1)
Usually minimum rather than exact. "unless you know otherwise always require a version greater than or equal to the version you're currenlty using" Well, you could also try an older one, to be nice? But I guess that's not encouraging people to update their packages..
Before namespaces were rolled out in R 2.14.0 you used Depends
instead of Imports
, which you should now use almost in all cases except when not---which will be covered in namespaces.
You can use Depends: R (>= 3.0.1)
but not at all clear what he means by devtools::create()
will do this for you. This seems to no longer be true.
Short and long versions of what your package does. But also add a README.md
for a longer description
Use:
person("Maja", "Zaloznik", email = "maja.zaloznik@gmail.com", role = c("aut", "cre"))
Also might be useful to add URL:
and BugReports:
e.g. the github repo's issues.
Either the acronym, or write file LICENCE
and then write sth in that file.
Open source licences to conside:
MIT + file LICENCE
, where the file includes the year and name of copyright holder. This one lets everoneuse and distribute as long as they attribure it and add a copy of the original licence. But they can distribute it under any licence they likeGPL-2
and GPL-3
are copy-left, require that even modifications are under GPL as well (if they are distributed). So if your package or part of the code is reused, the new developer must make the source available as well.
CC0
relinquishes all rights, puts it in the public domain. really only usefull for data packages.
Standard versioning major.minor.patch
, while in development use 0.0.0.9000
to make that clear before it's released. if that's so, then why doesn't create()
do that instead of 0.1.0.
?
Is only helpful if you know what you're looking for.
Instead of manually writing .Rd
files for each function, roxygen2
lets you write literate code with comments alongside the functions, which are then automatically converted to .Rd
files. (also you use it for the collate field and the namespace, but more on that later)
So write roxygen comments in your source file, run devtools::document()
to generate the .Rd
files, preview the documentation with ?
. But the previews created this way will not show any links between pages.For that use:
Add roxygen comments, click the build and reload
button in the build pane or pres Ctrl+shift+B
. This is a lot more thorough: it completley rebuilds the package, which also updates the documentation, and then installs it in your regular library, restarts R and reloads the package. This is now called install and restart
Are placed before the function they refer to, the blocks of text are broken up into @tags
, The first three sections are implicit, so you don't need to add the tags:
@title
the first sentence is the title@description
is the second paragraph@details
the third and subsequent paragraphs are details. this is missing from the footnoteYou can use formatting e.g. \code{}
and \link{}
What's the deal with the default outputs? OK, html_vingette
as opposed to html_document
is explained here to be due to its smaller size i.e. great for CRAN.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.