Commit f0ff916e authored by Andreas Tille's avatar Andreas Tille

New upstream version 1.2

parents
Package: rprojroot
Title: Finding Files in Project Subdirectories
Version: 1.2
Authors@R: person(given = "Kirill", family = "Müller", role = c("aut",
"cre"), email = "krlmlr+r@mailbox.org")
Description: Robust, reliable and flexible paths to files below a
project root. The 'root' of a project is defined as a directory
that matches a certain criterion, e.g., it contains a certain
regular file.
Depends: R (>= 3.0.0)
Imports: backports
Suggests: testthat, knitr, withr, rmarkdown
VignetteBuilder: knitr
License: GPL-3
LazyData: true
Encoding: UTF-8
URL: https://github.com/krlmlr/rprojroot,
https://krlmlr.github.io/rprojroot
BugReports: https://github.com/krlmlr/rprojroot/issues
RoxygenNote: 5.0.1.9000
Collate: 'rrmake.R' 'criterion.R' 'file.R' 'has-file.R' 'root.R'
'rprojroot-package.R' 'shortcut.R'
NeedsCompilation: no
Packaged: 2017-01-16 11:50:23 UTC; muelleki
Author: Kirill Müller [aut, cre]
Maintainer: Kirill Müller <krlmlr+r@mailbox.org>
Repository: CRAN
Date/Publication: 2017-01-16 14:16:04
09fc8e111b014442f6569ac39cfff43f *DESCRIPTION
04765b617541cee07297676a210d25d3 *NAMESPACE
2d0469a219c83ed66c4e2695d4ff2d1c *NEWS.md
da5b8d6d5d1ea8a0aa1c4a1cfed314ff *R/criterion.R
3283d4a6f17606ad0193bace804ed012 *R/file.R
1050ecdd048dce13bbf81615de85489f *R/has-file.R
d87a8501549770bd80068475e78671fe *R/root.R
0688a81a507c7639e2b2cec0edad2dbf *R/rprojroot-package.R
dd227290335dea57e2a1031a5b5beb35 *R/rrmake.R
4de9a9bbf48372e1505dd1550bee0418 *R/shortcut.R
0415d9530ea3a983814164d735e5db9e *build/vignette.rds
0634afa63b0e9c96760bcdce54655942 *inst/doc/rprojroot.R
7855fbb459b9e137bc458294b20db80c *inst/doc/rprojroot.Rmd
9e32800aeeef65fcddc218dcaa367682 *inst/doc/rprojroot.html
6a14df38876e8de0b3a24e689aa98cf7 *man/criteria.Rd
df64533edc92ee73830a13070b418d54 *man/find_root.Rd
5accd629c3b4aa22c6a484b2a52ed0a2 *man/find_root_file.Rd
4a89be5e4f191328f2a7e8b6f31a8ba6 *man/root_criterion.Rd
a7d033b76a25c7abfb137de00b3da294 *man/rprojroot-package.Rd
1d802d92f687ffbb11ee26ae68396d94 *tests/testthat.R
3e6320af0b5c1a4ab2bc220759c31553 *tests/testthat/hierarchy/DESCRIPTION
d41d8cd98f00b204e9800998ecf8427e *tests/testthat/hierarchy/a/b/a
b16de7308a1f8d6f4f98b1a558ea957d *tests/testthat/hierarchy/a/b/b
d41d8cd98f00b204e9800998ecf8427e *tests/testthat/hierarchy/a/b/c/d
d41d8cd98f00b204e9800998ecf8427e *tests/testthat/hierarchy/a/remake.yml
55e116ea754236d78e4e6342adbe3661 *tests/testthat/hierarchy/b
d41d8cd98f00b204e9800998ecf8427e *tests/testthat/hierarchy/c
3936ed002c1dd1fa5dc7a13d99d5df86 *tests/testthat/hierarchy/hierarchy.Rproj
d41d8cd98f00b204e9800998ecf8427e *tests/testthat/package/DESCRIPTION
d41d8cd98f00b204e9800998ecf8427e *tests/testthat/package/tests/testthat.R
d41d8cd98f00b204e9800998ecf8427e *tests/testthat/package/tests/testthat/test-something.R
06e74a052aa3a2ab9a51a5135802a55b *tests/testthat/test-criterion.R
ce3ca96d5d38e1c0972d639a81f659c1 *tests/testthat/test-make.R
006eb32f50098d057c0a9cfb6ac4d0ba *tests/testthat/test-root.R
ec33c4973b9fac9b680322d60d6807ac *tests/testthat/test-testthat.R
d8879d25ee21b0f84992020d95500aaf *tests/testthat/vcs/git.zip
28e9555632b66c264d026cd065dda0db *tests/testthat/vcs/svn.zip
7855fbb459b9e137bc458294b20db80c *vignettes/rprojroot.Rmd
# Generated by roxygen2: do not edit by hand
S3method("|",root_criterion)
S3method(as.root_criterion,character)
S3method(as.root_criterion,default)
S3method(as.root_criterion,root_criterion)
S3method(format,root_criterion)
S3method(print,root_criterion)
S3method(str,root_criteria)
export(as.root_criterion)
export(criteria)
export(find_package_root_file)
export(find_remake_root_file)
export(find_root)
export(find_root_file)
export(find_rstudio_root_file)
export(find_testthat_root_file)
export(from_wd)
export(get_root_desc)
export(has_dir)
export(has_dirname)
export(has_file)
export(has_file_pattern)
export(is.root_criterion)
export(is_git_root)
export(is_projectile_project)
export(is_r_package)
export(is_remake_project)
export(is_rstudio_project)
export(is_svn_root)
export(is_testthat)
export(is_vcs_root)
export(root_criterion)
import(backports)
importFrom(utils,str)
# rprojroot 1.2 (2017-01-15)
- New root criteria
- `is_projectile_project` recognize projectile projects (#21).
- `has_dir()` constructs root criteria that check for existence of a directory.
- `is_git_root`, `is_svn_root` and `is_vcs_root` look for a version control system root (#19).
- New function
- `get_root_desc()` returns the description of the criterion that applies to a given root, useful for composite criteria created with `|`.
- Minor enhancements
- Improve formatting of alternative criteria (#18).
- If root cannot be found, the start path is shown in the error message.
- Internal
- The `$testfun` member of the `rprojroot` S3 class is now a list of functions instead of a function.
# rprojroot 1.1 (2016-10-29)
- Compatibility
- Compatible with R >= 3.0.0 with the help of the `backports` package.
- New root criteria
- `is_remake_project` and `find_remake_root_file()` look for [remake](https://github.com/richfitz/remake) project (#17).
- `is_testthat` and `find_testthat_root_file()` that looks for `tests/testthat` root (#14).
- `from_wd`, useful for creating accessors to a known path (#11).
- Minor enhancement
- Criteria can be combined with the `|` operator (#15).
- Documentation
- Add package documentation with a few examples (#13).
- Clarify difference between `find_file()` and `make_fix_file()` in vignette (#9).
- Remove unexported functions from documentation and examples (#10).
- Use `pkgdown` to create website.
- Testing
- Use Travis instead of wercker. Travis tests three R versions, and OS X.
- Improve AppVeyor testing.
# rprojroot 1.0-2 (2016-03-28)
- Fix test that fails on Windows only on CRAN.
# rprojroot 1.0 (2016-03-26)
Initial CRAN release.
- S3 class `root_criterion`:
- Member functions: `find_file()` and `make_fix_file()`
- `root_criterion()`
- `as.root_criterion()`
- `is.root_criterion()`
- `has_file()`
- `has_file_pattern()`
- Built-in criteria:
- `is_r_package`
- `is_rstudio_project`
- Getting started:
- `find_package_root_file()`
- `find_rstudio_root_file()`
- Use a custom notion of a project root:
- `find_root()`
- `find_root_file()`
- Vignette
#' Is a directory the project root?
#'
#' Objects of the `root_criterion` class decide if a
#' given directory is a project root.
#'
#' Construct criteria using `root_criterion` in a very general fashion
#' by specifying a function with a `path` argument, and a description.
#'
#' @param testfun A function with one parameter that returns `TRUE`
#' if the directory specified by this parameter is the project root,
#' and `FALSE` otherwise. Can also be a list of such functions.
#' @param desc A textual description of the test criterion, of the same length
#' as `testfun`
#' @param subdir Subdirectories to start the search in, if found
#'
#' @return
#' An S3 object of class `root_criterion` wit the following members:
#'
#' @include rrmake.R
#' @export
#'
#' @examples
#' root_criterion(function(path) file.exists(file.path(path, "somefile")), "has somefile")
#' has_file("DESCRIPTION")
#' is_r_package
#' is_r_package$find_file
#' \dontrun{
#' is_r_package$make_fix_file(".")
#' }
root_criterion <- function(testfun, desc, subdir = NULL) {
testfun <- check_testfun(testfun)
stopifnot(length(desc) == length(testfun))
full_desc <- paste0(
desc,
if (!is.null(subdir)) paste0(
" (also look in subdirectories: ",
paste0("`", subdir, "`", collapse = ", "),
")"
)
)
criterion <- structure(
list(
#' @return
#' \describe{
#' \item{`testfun`}{The `testfun` argument}
testfun = testfun,
#' \item{`desc`}{The `desc` argument}
desc = full_desc,
#' \item{`subdir`}{The `subdir` argument}
subdir = subdir
),
class = "root_criterion"
)
#' \item{`find_file`}{A function with `...` argument that returns
#' for a path relative to the root specified by this criterion.
#' The optional `path` argument specifies the starting directory,
#' which defaults to `"."`.
#' }
criterion$find_file <- make_find_root_file(criterion)
#' \item{`make_fix_file`}{A function with a `path` argument that
#' returns a function that finds paths relative to the root. For a
#' criterion `cr`, the result of `cr$make_fix_file(".")(...)`
#' is identical to `cr$find_file(...)`. The function created by
#' `make_fix_file` can be saved to a variable to be more independent
#' of the current working directory.
#' }
#' }
criterion$make_fix_file <-
function(path = getwd()) make_fix_root_file(criterion, path)
criterion
}
check_testfun <- function(testfun) {
if (is.function(testfun)) {
testfun <- list(testfun)
}
for (f in testfun) {
if (!isTRUE(all.equal(names(formals(f)), "path"))) {
stop("All functions in testfun must have exactly one argument 'path'")
}
}
testfun
}
#' @rdname root_criterion
#' @param x An object
#' @export
is.root_criterion <- function(x) {
inherits(x, "root_criterion")
}
#' @rdname root_criterion
#' @export
as.root_criterion <- function(x) UseMethod("as.root_criterion", x)
#' @details
#' The `as.root_criterion` function accepts objects of class
#' `root_criterion`, and character values; the latter will be
#' converted to criteria using `has_file`.
#'
#' @rdname root_criterion
#' @export
as.root_criterion.character <- function(x) {
has_file(x)
}
#' @rdname root_criterion
#' @export
as.root_criterion.root_criterion <- identity
#' @export
as.root_criterion.default <- function(x) {
stop("Cannot coerce ", x, " to type root_criterion.")
}
#' @export
format.root_criterion <- function(x, ...) {
if (length(x$desc) > 1) {
c("Root criterion: one of", paste0("- ", x$desc))
} else {
paste0("Root criterion: ", x$desc)
}
}
#' @export
print.root_criterion <- function(x, ...) {
cat(format(x), sep = "\n")
invisible(x)
}
#' @export
#' @rdname root_criterion
#' @details Root criteria can be combined with the `|` operator. The result is a
#' composite root criterion that requires either of the original criteria to
#' match.
#' @param y An object
`|.root_criterion` <- function(x, y) {
stopifnot(is.root_criterion(y))
root_criterion(
c(x$testfun, y$testfun),
c(x$desc, y$desc)
)
}
#' File paths relative to the root of a directory hierarchy
#'
#' Append an arbitrary number of path components to the root using
#' [base::file.path()].
#'
#' The `find_root_file` function is a simple wrapper around
#' [find_root()] that
#' appends an arbitrary number of path components to the root using
#' [base::file.path()].
#'
#' @param criterion A criterion, will be coerced using
#' [as.root_criterion()]
#' @param path The start directory
#' @param ... Further path components passed to [file.path()]
#' @return The normalized path of the root as specified by the search criteria,
#' with the additional path components appended.
#' Throws an error if no root is found
#'
#' @examples
#' \dontrun{
#' find_package_root_file("tests", "testthat.R")
#' has_file("DESCRIPTION", "^Package: ")$find_file
#' has_file("DESCRIPTION", "^Package: ")$make_fix_file(".")
#' }
#'
#' @seealso [find_root()] [utils::glob2rx()] [base::file.path()]
#'
#' @export
find_root_file <- function(..., criterion, path = ".") {
root <- find_root(criterion = criterion, path = path)
file.path(root, ...)
}
format_lines <- function(n) {
if (n == 1) "line" else paste0(n, " lines")
}
#' @details
#' The `has_file` function constructs a criterion that checks for the
#' existence of a specific file (which itself can be in a subdirectory of the
#' root) with specific contents.
#'
#' @rdname root_criterion
#' @param filepath File path (can contain directories)
#' @param contents Regular expression to match the file contents
#' @inheritParams base::readLines
#' @export
has_file <- function(filepath, contents = NULL, n = -1L) {
force(filepath)
force(contents)
force(n)
testfun <- eval(bquote(function(path) {
testfile <- file.path(path, .(filepath))
if (!file.exists(testfile))
return(FALSE)
if (is_dir(testfile))
return(FALSE)
match_contents(testfile, .(contents), .(n))
}))
desc <- paste0(
"contains a file `", filepath, "`",
if (!is.null(contents)) {
paste0(" with contents matching `", contents, "`",
if (n >= 0L) paste0(" in the first ", format_lines(n)))
})
root_criterion(testfun, desc)
}
#' @details
#' The `has_dir` function constructs a criterion that checks for the
#' existence of a specific directory.
#'
#' @rdname root_criterion
#' @export
has_dir <- function(filepath) {
force(filepath)
testfun <- eval(bquote(function(path) {
testfile <- file.path(path, .(filepath))
if (!file.exists(testfile))
return(FALSE)
is_dir(testfile)
}))
desc <- paste0("contains a directory `", filepath, "`")
root_criterion(testfun, desc)
}
#' @details
#' The `has_file_pattern` function constructs a criterion that checks for the
#' existence of a file that matches a pattern, with specific contents.
#'
#' @rdname root_criterion
#' @param pattern Regular expression to match the file name
#' @inheritParams base::readLines
#' @export
has_file_pattern <- function(pattern, contents = NULL, n = -1L) {
force(pattern)
force(contents)
force(n)
testfun <- eval(bquote(function(path) {
files <- list_files(path, .(pattern))
for (f in files) {
if (!match_contents(f, .(contents), .(n))) {
next
}
return(TRUE)
}
return(FALSE)
}))
desc <- paste0(
"contains a file matching `", pattern, "`",
if (!is.null(contents)) {
paste0(" with contents matching `", contents, "`",
if (n >= 0L) paste0(" in the first ", format_lines(n)))
})
root_criterion(testfun, desc)
}
#' @details
#' The `has_dirname` function constructs a criterion that checks if the
#' [base::dirname()] has a specific name.
#'
#' @rdname root_criterion
#' @param dirname A directory name, without subdirectories
#' @export
has_dirname <- function(dirname, subdir = NULL) {
force(dirname)
testfun <- eval(bquote(function(path) {
dir.exists(file.path(dirname(path), .(dirname)))
}))
desc <- paste0("directory name is `", dirname, "`")
root_criterion(testfun, desc, subdir = subdir)
}
#' @export
is_rstudio_project <- has_file_pattern("[.]Rproj$", contents = "^Version: ", n = 1L)
#' @export
is_r_package <- has_file("DESCRIPTION", contents = "^Package: ")
#' @export
is_remake_project <- has_file("remake.yml")
#' @export
is_projectile_project <- has_file(".projectile")
#' @export
is_git_root <- has_dir(".git")
#' @export
is_svn_root <- has_dir(".svn")
#' @export
is_vcs_root <- is_git_root | is_svn_root
#' @export
is_testthat <- has_dirname("testthat", c("tests/testthat", "testthat"))
#' @export
from_wd <- root_criterion(function(path) TRUE, "from current working directory")
#' Prespecified criteria
#'
#' This is a collection of commonly used root criteria.
#'
#' @export
criteria <- structure(
list(
is_rstudio_project = is_rstudio_project,
is_r_package = is_r_package,
is_remake_project = is_remake_project,
is_projectile_project = is_projectile_project,
is_git_root = is_git_root,
is_svn_root = is_svn_root,
is_vcs_root = is_vcs_root,
is_testthat = is_testthat,
from_wd = from_wd
),
class = "root_criteria")
#' @export
#' @importFrom utils str
str.root_criteria <- function(object, ...) {
str(lapply(object, format))
}
#' @details
#' `is_rstudio_project` looks for a file with extension `.Rproj`.
#'
#' @rdname criteria
#' @export
"is_rstudio_project"
#' @details
#' `is_r_package` looks for a `DESCRIPTION` file.
#'
#' @rdname criteria
#' @export
"is_r_package"
#' @details
#' `is_remake_project` looks for a `remake.yml` file.
#'
#' @rdname criteria
#' @export
"is_remake_project"
#' @details
#' `is_projectile_project` looks for a `.projectile` file.
#'
#' @rdname criteria
#' @export
"is_projectile_project"
#' @details
#' `is_git_project` looks for a `.git` directory.
#'
#' @rdname criteria
#' @export
"is_git_root"
#' @details
#' `is_svn_project` looks for a `.svn` directory.
#'
#' @rdname criteria
#' @export
"is_svn_root"
#' @details
#' `is_vcs_project` looks for the root of a version control
#' system, currently only Git and SVN are supported.
#'
#' @rdname criteria
#' @export
"is_vcs_root"
#' @details
#' `is_testthat` looks for the `testthat` directory, works when
#' developing, testing, and checking a package.
#'
#' @rdname criteria
#' @export
"is_testthat"
#' @details
#' `from_wd` uses the current working directory.
#'
#' @rdname criteria
#' @export
"from_wd"
list_files <- function(path, filename) {
files <- dir(path = path, pattern = filename, all.files = TRUE, full.names = TRUE)
dirs <- is_dir(files)
files <- files[!dirs]
files
}
is_dir <- function(x) {
dir.exists(x)
}
match_contents <- function(f, contents, n) {
if (is.null(contents)) {
return(TRUE)
}
fc <- readLines(f, n)
any(grepl(contents, fc))
}
#' Find the root of a directory hierarchy
#'
#' A \emph{root} is defined as a directory that contains a regular file
#' whose name matches a given pattern and which optionally contains a given text.
#' The search for a root starts at a given directory (the working directory
#' by default), and proceeds up the directory hierarchy.
#'
#' Starting from the working directory, the `find_root` function searches
#' for the root.
#' If a root is found, the `...` arguments are used to construct a path;
#' thus, if no extra arguments are given, the root is returned.
#' If no root is found, an error is thrown.
#'
#' @inheritParams find_root_file
#' @return The normalized path of the root as specified by the search criterion.
#' Throws an error if no root is found
#'
#' @examples
#' \dontrun{
#' find_root(glob2rx("DESCRIPTION"), "^Package: ")
#' }
#'
#' @seealso [utils::glob2rx()] [file.path()]
#'
#' @export
find_root <- function(criterion, path = ".") {
criterion <- as.root_criterion(criterion)
start_path <- get_start_path(path, criterion$subdir)
path <- start_path
for (i in seq_len(.MAX_DEPTH)) {
for (f in criterion$testfun) {
if (f(path)) {
return(path)
}
}
if (is_root(path)) {
stop("No root directory found in ", start_path, " or its parent directories. ",
paste(format(criterion), collapse = "\n"), call. = FALSE)
}
path <- dirname(path)
}
stop("Maximum search of ", .MAX_DEPTH, " exceeded. Last path: ", path)
}
.MAX_DEPTH <- 100L
get_start_path <- function(path, subdirs) {
path <- normalizePath(path, winslash = "/", mustWork = TRUE)
for (subdir in subdirs) {
subdir_path <- file.path(path, subdir)
if (dir.exists(subdir_path)) {
return(subdir_path)
}
}
path
}
# Borrowed from devtools
is_root <- function(path) {
identical(normalizePath(path, winslash = "/"),
normalizePath(dirname(path), winslash = "/"))
}
#' @rdname find_root
#' @description `get_root_desc()` returns the description of the criterion
#' for a root path. This is especially useful for composite root criteria
#' created with [|.root_criterion()].
#' @export
get_root_desc <- function(criterion, path) {
for (i in seq_along(criterion$testfun)) {
if (criterion$testfun[[i]](path)) {
return(criterion$desc[[i]])
}
}
stop("path is not a root. ",
paste(format(criterion), collapse = "\n"), call. = FALSE)
}
#' @details
#' See the "Value" section in [root_criterion()] for documentation
#' of root criterion objects, and [criteria()] for useful predefined
#' root criteria.
#'
#' @examples
#' criteria
#' \dontrun{
#' is_r_package$find_file("NAMESPACE")
#' root_fun <- is_r_package$make_fix_file()
#' root_fun("NAMESPACE")
#' }
#' @import backports
#' @api
"_PACKAGE"
make_find_root_file <- function(criterion) {
force(criterion)
eval(bquote(function(..., path = ".") {
find_root_file(..., criterion = criterion, path = path)
}))
}
make_fix_root_file <- function(criterion, path) {
root <- find_root(criterion = criterion, path = path)
eval(bquote(function(...) {
file.path(.(root), ...)
}))
}
#' @rdname find_root_file
#' @export
find_rstudio_root_file <- is_rstudio_project$find_file
#' @rdname find_root_file
#' @export
find_package_root_file <- is_r_package$find_file
#' @rdname find_root_file
#' @export
find_remake_root_file <- is_remake_project$find_file
#' @rdname find_root_file
#' @export
find_testthat_root_file <- is_testthat$find_file
## ------------------------------------------------------------------------
basename(getwd())
## ------------------------------------------------------------------------