Skip to content

Commit

Permalink
Merge 8a59b0b into bbe8467
Browse files Browse the repository at this point in the history
  • Loading branch information
robertzk committed Feb 22, 2015
2 parents bbe8467 + 8a59b0b commit ccc04b6
Show file tree
Hide file tree
Showing 19 changed files with 259 additions and 11 deletions.
7 changes: 7 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.travis.yml
data/.DS_Store
..Rcheck
.git
.gitignore
.travis.yml
NEWS.md
8 changes: 5 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ Authors@R: c(person("Robert", "Krzyzanowski",
email = "technoguyrob@gmail.com", role = c("aut", "cre")))
Depends:
R (>= 3.0.1)
License: MIT
License: GPL (>= 2)
LazyData: true
Imports:
stringr
stringr,
devtools
Suggests:
knitr,
microbenchmark,
testthat
testthat,
testthatsomemore
Roxygen: list(wrap = FALSE)
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Generated by roxygen2 (4.1.0.9000): do not edit by hand

import(devtools)
import(stringr)
2 changes: 1 addition & 1 deletion R/package.packagepackage.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
#' pkgapply(function(pkg) { ... })
#' }
#' @name packagepackage
#' @import stringr
#' @import stringr devtools
#' @docType package
NULL
4 changes: 0 additions & 4 deletions R/pending.R

This file was deleted.

74 changes: 74 additions & 0 deletions R/pkgpapply.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#' Apply an operation over several packages.
#'
#' This function is meant to be used by R developers that have to make
#' simultaneous updates to many packages.
#'
#' @note
#' The argument \code{pkg} received by the function call on each iteration is
#' \code{devtools::as.package(pkg_path)}, which has a \code{$path} available
#' and whose other attributes originate directly from the package's DESCRIPTION
#' file.
#' @param packages character. A character vector of package names.
#' These must be directories relative to \code{dir}.
#' @param dir character. The root directory on which to iterate over
#' all the packages. By default, the current working directory.
#' @param f function. The function to apply to each package.
#' The argument the function receives is
#' \code{devtools::as.package(pkg_path)}, where \code{pkg_path} is
#' the absolute path of the package. The output of this will be a list
#' with a \code{path} key, as well as keys corresponding to each
#' element in the DESCRIPTION file (\code{title}, \code{version}, etc.).
#' @return A list where each element is the result of \code{f} applied
#' to the respective package.
#' @seealso \code{\link[devtools]{as.package}}
#' @examples
#' \dontrun{
#' # You can use pkgapply to iterate over each package and apply some
#' # operation.
#' pkgapply(c('package1', 'package2'), dir = '/root/dir', function(pkg) { ... })
#' pkgapply(c('package1', 'package2'), function(pkg) { ... })
#'
#' # If you leave the package names blank, it will loop over all directories
#' # relative to `dir` that contain a DESCRIPTION file and are thus recognized
#' # as being R packages.
#' pkgapply(dir = '/root/dir', function(pkg) { ... })
#'
#' # If you do not provide a directory, the current directory will be used.
#' pkgapply(function(pkg) { ... })
#' }
pkgapply <- function(packages, dir = getwd(), f) {
if (is.function(packages) && missing(f)) { f <- packages; packages <- NULL }
if (is.function(dir)) { f <- dir; dir <- getwd() }

if (missing(packages) || is.null(packages)) {
packages <- Filter(is_package, list.files(dir, full.names = TRUE))
} else {
packages <- vapply(packages, prefix_package, character(1), dir)
}

packages <- lapply(packages, sanitize_package)

lapply(packages, f)
}

sanitize_package <- function(pkg) {
stopifnot(is.character(pkg) || devtools::is.package(pkg))
if (devtools::is.package(pkg)) { pkg }
else { devtools::as.package(pkg) }
}

prefix_package <- function(pkg, dir) {
if (devtools::is.package(pkg)) { return(pkg$path) }
if (!file.exists(pkg)) {
pkg <- file.path(dir, pkg)
}
if (!file.exists(pkg)) {
stop("No such package: ", sQuote(pkg), call. = FALSE)
}
pkg
}

is_package <- function(pkg) {
file.exists(file.path(pkg, 'DESCRIPTION'))
}

55 changes: 55 additions & 0 deletions man/pkgapply.Rd
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
% Generated by roxygen2 (4.1.0.9000): do not edit by hand
% Please edit documentation in R/pkgpapply.R
\name{pkgapply}
\alias{pkgapply}
\title{Apply an operation over several packages.}
\usage{
pkgapply(packages, dir = getwd(), f)
}
\arguments{
\item{packages}{character. A character vector of package names.
These must be directories relative to \code{dir}.}

\item{dir}{character. The root directory on which to iterate over
all the packages. By default, the current working directory.}

\item{f}{function. The function to apply to each package.
The argument the function receives is
\code{devtools::as.package(pkg_path)}, where \code{pkg_path} is
the absolute path of the package. The output of this will be a list
with a \code{path} key, as well as keys corresponding to each
element in the DESCRIPTION file (\code{title}, \code{version}, etc.).}
}
\value{
A list where each element is the result of \code{f} applied
to the respective package.
}
\description{
This function is meant to be used by R developers that have to make
simultaneous updates to many packages.
}
\note{
The argument \code{pkg} received by the function call on each iteration is
\code{devtools::as.package(pkg_path)}, which has a \code{$path} available
and whose other attributes originate directly from the package's DESCRIPTION
file.
}
\examples{
\dontrun{
# You can use pkgapply to iterate over each package and apply some
# operation.
pkgapply(c('package1', 'package2'), dir = '/root/dir', function(pkg) { ... })
# If you leave the package names blank, it will loop over all directories
# relative to `dir` that contain a DESCRIPTION file and are thus recognized
# as being R packages.
pkgapply(dir = '/root/dir', function(pkg) { ... })
# If you do not provide a directory, the current directory will be used.
pkgapply(function(pkg) { ... })
}
}
\seealso{
\code{\link[devtools]{as.package}}
}
10 changes: 10 additions & 0 deletions tests/testthat/helper-with_packages.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# pkgs will be a character vector of packages.
with_packages <- function(expr) {
dir <- file.path(getwd(), 'packages')
outdir <- tempdir()
on.exit(unlink(outdir))
file.copy(recursive = TRUE, dir, outdir)
pkgs <- list.files(file.path(outdir, 'packages'), full.names = TRUE)
eval_env <- list2env(list(pkgs = pkgs), parent = parent.frame())
eval(substitute(expr), envir = eval_env)
}
3 changes: 3 additions & 0 deletions tests/testthat/packages/package1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.Rproj.user
.Rhistory
.RData
10 changes: 10 additions & 0 deletions tests/testthat/packages/package1/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Package: package1
Title: This is a package
Version: 0.1
Authors@R: c(person("Robert", "Krzyzanowski",
email = "technoguyrob@gmail.com", role = c("aut", "cre")))
Description: It does package.
Depends: R (5.0)
License: MIT
Suggests: testthat, testthatsomemore, knitr
LazyData: true
1 change: 1 addition & 0 deletions tests/testthat/packages/package1/NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exportPattern("^[^\\.]")
3 changes: 3 additions & 0 deletions tests/testthat/packages/package2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.Rproj.user
.Rhistory
.RData
10 changes: 10 additions & 0 deletions tests/testthat/packages/package2/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Package: package2
Title: This is another package
Version: 0.1
Authors@R: c(person("Robert", "Krzyzanowski",
email = "technoguyrob@gmail.com", role = c("aut", "cre")))
Description: It does package.
Depends: R (5.0)
License: MIT
Suggests: testthat, testthatsomemore, knitr
LazyData: true
1 change: 1 addition & 0 deletions tests/testthat/packages/package2/NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exportPattern("^[^\\.]")
3 changes: 3 additions & 0 deletions tests/testthat/packages/package3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.Rproj.user
.Rhistory
.RData
10 changes: 10 additions & 0 deletions tests/testthat/packages/package3/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Package: package3
Title: Woah it is another package
Version: 0.1
Authors@R: c(person("Robert", "Krzyzanowski",
email = "technoguyrob@gmail.com", role = c("aut", "cre")))
Description: It does package.
Depends: R (5.0)
License: MIT
Suggests: testthat, testthatsomemore, knitr
LazyData: true
1 change: 1 addition & 0 deletions tests/testthat/packages/package3/NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exportPattern("^[^\\.]")
3 changes: 0 additions & 3 deletions tests/testthat/test-pending.R

This file was deleted.

64 changes: 64 additions & 0 deletions tests/testthat/test-pkgapply.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
context('pkgapply')
library(testthatsomemore)

describe("iterating using a function", {
test_that("it does nothing when passed no packages", {
assert(pkgapply(c(), identity))
})

test_that("it errors when given a non character package", {
expect_error(pkgapply(5, identity))
})

test_that("it errors when the package doesn't exist", {
expect_error(pkgapply("boop", identity), "No such package")
})

describe("changing the DESCRIPTION file", {
change_description <- function(..., check = pkgs, prelude = NULL) {
eval.parent(substitute({
with_packages({
prelude
pkgapply(..., function(pkg) {
DESCRIPTION_path <- file.path(pkg$path, 'DESCRIPTION')
DESCRIPTION <- readLines(DESCRIPTION_path)
DESCRIPTION <- gsub("Depends: R \\([^(]+\\)", "Depends: R (5.0)", DESCRIPTION)
writeLines(DESCRIPTION, DESCRIPTION_path)
})

for (pkg in check) {
expect_equal(devtools::as.package(pkg)$depends, "R (5.0)")
}
})
}))
}

test_that("it can make a trivial change to each package's DESCRIPTION", {
change_description(pkgs)
})

test_that("it can be given S3 package objects", {
change_description(lapply(pkgs, devtools::as.package))
})

test_that("it can focus on the packages it is told to focus on", {
change_description(p <- file.path(dirname(pkgs[1]), c("package1", "package3")), check = p)
})

test_that("it can be given an explicit dir", {
change_description(p <- c("package1", "package3"),
dir = dir <- dirname(pkgs[1]), check = file.path(dir, p))
})

test_that("it can be given just a dir", {
change_description(dir = dir <- dirname(pkgs[1]),
check = file.path(dir, paste0("package", 1:3)))
})

test_that("it can be given just a function", {
change_description(check = file.path(dirname(pkgs[1]), paste0("package", 1:3)),
prelude = setwd(dirname(pkgs[1])))
})
})
})

0 comments on commit ccc04b6

Please sign in to comment.