Skip to content

Commit

Permalink
Merge pull request #91 from reconverse/04
Browse files Browse the repository at this point in the history
04
  • Loading branch information
TimTaylor authored Aug 9, 2021
2 parents 267d92c + 6262868 commit 8127f49
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 58 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: reportfactory
Title: Lightweight Infrastructure for Handling Multiple R Markdown Documents
Version: 0.3.1.9000
Version: 0.4.0
Authors@R: c(
person("Thibaut", "Jombart", role = "aut", email = "thibautjombart@gmail.com"),
person("Amy", "Gimma", role = "ctb", email = "amyg225@gmail.com"),
Expand Down
4 changes: 3 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# reportfactory (development version)
# reportfactory 0.4.0

* `list_deps()` now only checks the `report_sources` and `scripts` folders for
package dependencies.
* `list_deps()` no longer uses the `checkpoint` package so the dependency has
been dropped.

Expand Down
6 changes: 6 additions & 0 deletions R/internals.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ factory_root <- function(directory = ".") {
root
}

# -------------------------------------------------------------------------

#' check whether a vector is "integer-like" to a given precision
#'
#' @param x vector to check
Expand All @@ -47,6 +49,8 @@ is.wholenumber <- function(x, tol = .Machine$double.eps^0.5) {
all(abs(x - round(x)) < tol)
}

# -------------------------------------------------------------------------

#' copy a file from the skeleton directory
#'
#' @param file name of the file you want to copy
Expand All @@ -58,6 +62,7 @@ copy_skeleton_file <- function(file, dest) {
file.copy(f, dest)
}

# -------------------------------------------------------------------------

#' Change part of the front yaml matter from an Rmarkdown file
#'
Expand Down Expand Up @@ -103,6 +108,7 @@ change_yaml_matter <- function(input_file, ..., output_file) {
}
}

# -------------------------------------------------------------------------

#' Return rows of one data.frame not in another
#'
Expand Down
113 changes: 77 additions & 36 deletions R/list_deps.R
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#' List dependencies of reports within a factory
#'
#' This function can be used to list package dependencies based of the reports
#' and R scripts within the factory.
#' List package dependencies based on the reports and scripts within the
#' report_sources and scripts directories respectively.
#'
#' @inheritParams compile_reports
#' @param missing A logical indicating if only missing dependencies should be
Expand All @@ -11,55 +11,92 @@
#' checked. Note that this will error if the script cannot be parsed.
#' @param exclude_readme If TRUE (default) README files will not be checked for
#' dependencies.
#' @param parse_first If `TRUE` code will first be parsed for validity and
#' unevaluated Rmd chunks will not be checked for dependencies. The default
#' value is `FALSE` and, in this case, files will simply be checked line by
#' line for calls to `library`, `require` or use of double, `::`, and triple,
#' `:::` function calls.
#'
#' @note This function requires that any R scripts present in the factory are
#' valid syntax else the function will error.
#'
#' @return A character vector of package dependencies.
#'
#' @export
list_deps <- function(factory = ".", missing = FALSE, check_r = TRUE, exclude_readme = TRUE) {
list_deps <- function(factory = ".", missing = FALSE, check_r = TRUE,
exclude_readme = TRUE, parse_first = FALSE) {

tmp <- suppressMessages(validate_factory(factory))
root <- tmp$root
config <- as.data.frame(read.dcf(file.path(root, "factory_config")))
report_sources <- config$report_sources
root_report_sources <- file.path(root, report_sources)
root_scripts <- file.path(root, "scripts")
root_to_check <- c(root_report_sources, root_scripts)

# Find dependencies in R files
r_files <- list.files(root, pattern = "\\.[Rr]$", recursive = TRUE, full.names = TRUE)
# List of R files
r_files <- list.files(
root_to_check,
pattern = "\\.[Rr]$",
recursive = TRUE,
full.names = TRUE
)

# List of Rmd files
rmd_files <- list.files(
root_to_check, pattern = "\\.[Rr]md$",
recursive = TRUE,
full.names = TRUE
)
if (exclude_readme) {
readme <- list.files(
pattern = "README\\.Rmd",
recursive = TRUE,
ignore.case = TRUE,
full.names = TRUE
)
rmd_files <- rmd_files[!rmd_files %in% readme]
}

# Find R file dependencies
r_files_deps <- character(0)
if (length(r_files) && check_r) {
r_files_deps <- list_r_file_deps(r_files)
r_files_deps <- list_r_file_deps(r_files, parse = parse_first)
}

# Find dependencies in Rmd files. We knit the files first to ensure only
# dependencies of code that is actually run are returned.
# Find Rmd file dependencies
op <- options(knitr.purl.inline = TRUE)
on.exit(options(op))
rmd_files <- list.files(root, pattern = "\\.[Rr]md$", recursive = TRUE, full.names = TRUE)
if (exclude_readme) {
readme <- list.files(pattern = "README\\.Rmd", recursive = TRUE, ignore.case = TRUE, full.names = TRUE)
rmd_files <- rmd_files[!rmd_files %in% readme]
}

rmd_files_deps <- character(0)
if (length(rmd_files)) {
d <- tempdir()
fd <- sub(pattern = "(.*)\\..*$", replacement = "\\1", basename(rmd_files))
fd <- vapply(fd, function(x) file.path(d, x), character(1))
on.exit(unlink(fd), add = TRUE)
fefil <- tempfile()
on.exit(unlink(fefil), add = TRUE)
fe <- file(fefil, "w")
sink(fe, type = "message")
mapply(
function(x,y) try(knitr::purl(input = x, output = y, quiet = TRUE, documentation = 0), silent = TRUE),
rmd_files,
fd
)
sink(type = "message")
close(fe)
rmd_files_deps <- c("rmarkdown", list_r_file_deps(fd))
if (parse_first) {
d <- tempdir()
fd <- sub(pattern = "(.*)\\..*$", replacement = "\\1", basename(rmd_files))
fd <- vapply(fd, function(x) file.path(d, x), character(1))
on.exit(unlink(fd), add = TRUE)
fefil <- tempfile()
on.exit(unlink(fefil), add = TRUE)
fe <- file(fefil, "w")
sink(fe, type = "message")
mapply(
function(x,y) {
try(
knitr::purl(input = x, output = y, quiet = TRUE, documentation = 0),
silent = TRUE
)
} ,
rmd_files,
fd
)
sink(type = "message")
close(fe)
rmd_files_deps <- c("rmarkdown", list_r_file_deps(fd, parse = TRUE))
} else {
rmd_files_deps <- c("rmarkdown", list_r_file_deps(rmd_files, parse = FALSE))
}
}

# return unique dependencies
deps <- unique(c(r_files_deps, rmd_files_deps))
if (missing) {
installed <- basename(find.package(deps))
Expand All @@ -69,13 +106,17 @@ list_deps <- function(factory = ".", missing = FALSE, check_r = TRUE, exclude_re
deps
}

list_r_file_deps <- function(filepaths) {
# -------------------------------------------------------------------------

dat <- vapply(
filepaths,
function(x) paste(as.character(parse(x)), collapse = "\n"),
character(1)
)
list_r_file_deps <- function(filepaths, parse = FALSE) {

if (parse) {
f <- function(x) paste(as.character(parse(x)), collapse = "\n")
} else {
f <- function(x) paste(readLines(x), collapse = "\n")
}

dat <- vapply(filepaths, f, character(1))

colon_string <- r"---{([a-zA-Z][\w.]*)(?=:{2,3})}---"
colon_greg <- gregexpr(colon_string, dat, perl = TRUE)
Expand Down
6 changes: 2 additions & 4 deletions cran-comments.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Reason for update
* Fixes due to breaking changes in the upstream checkpoint package.

# Tested on
* Fedora 34, R Under development (unstable) (2021-06-16 r80504)
* Fedora 34, R Under development (unstable) (2021-08-09 r80724)
* Winbuilder, R Under development (unstable) (2021-08-05 r80717)

## R CMD check results for above environments
Status: OK
Expand Down
4 changes: 4 additions & 0 deletions inst/skeletons/example_report.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ output:
knitr::opts_chunk$set(echo = TRUE, collapse = TRUE, fig.width = 8, fig.height = 6, dpi = 70)
```

```{r, eval = FALSE}
base::mean(c(1,2))
```

This is an example report to illustrate how `reportfactory` can be used.
It loads the `fs` library purely for demonstration purposes.

Expand Down
13 changes: 10 additions & 3 deletions man/list_deps.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions tests/testthat/test-compile_reports.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test_that("test parameteriesed report output", {
md_file <- grep("\\.md", list_outputs(f), value = TRUE)
md_file <- path(f, "outputs", md_file)

expect_snapshot_file(md_file, "param_report_check.md", binary = FALSE)
expect_snapshot_file(md_file, "param_report_check.md", compare = compare_file_text)
})

test_that("test ignore_case works report output", {
Expand Down Expand Up @@ -54,7 +54,7 @@ test_that("test ignore_case works report output", {
md_file <- grep("\\.md", list_outputs(f), value = TRUE)
md_file <- path(f, "outputs", md_file)

expect_snapshot_file(md_file, "param_report_check.md", binary = FALSE)
expect_snapshot_file(md_file, "param_report_check.md", compare = compare_file_text)

# Should error if not case insensitive
expect_error(
Expand Down Expand Up @@ -98,7 +98,7 @@ test_that("test output folder gets recreated if not there", {
md_file <- grep("\\.md", list_outputs(f), value = TRUE)
md_file <- path(f, "outputs", md_file)

expect_snapshot_file(md_file, "outputs_deleted_param_report_check.md", binary = FALSE)
expect_snapshot_file(md_file, "outputs_deleted_param_report_check.md", compare = compare_file_text)
})


Expand Down Expand Up @@ -127,7 +127,7 @@ test_that("parameteriesed report with missing param output but input", {
md_file <- grep("\\.md", list_outputs(f), value = TRUE)
md_file <- path(f, "outputs", md_file)

expect_snapshot_file(md_file, "missing_param_report_check.md", binary = FALSE)
expect_snapshot_file(md_file, "missing_param_report_check.md", compare = compare_file_text)
})

test_that("non parameteriesed report with param input", {
Expand Down Expand Up @@ -155,7 +155,7 @@ test_that("non parameteriesed report with param input", {
md_file <- grep("\\.md", list_outputs(f), value = TRUE)
md_file <- path(f, "outputs", md_file)

expect_snapshot_file(md_file, "nonparameterised_with_params.md", binary = FALSE)
expect_snapshot_file(md_file, "nonparameterised_with_params.md", compare = compare_file_text)
})

test_that("parameteriesed report with missing param (but in environment)", {
Expand Down Expand Up @@ -184,7 +184,7 @@ test_that("parameteriesed report with missing param (but in environment)", {
md_file <- grep("\\.md", list_outputs(f), value = TRUE)
md_file <- path(f, "outputs", md_file)

expect_snapshot_file(md_file, "missing_param_but_envir.md", binary = FALSE)
expect_snapshot_file(md_file, "missing_param_but_envir.md", compare = compare_file_text)
})


Expand Down
38 changes: 31 additions & 7 deletions tests/testthat/test-list_deps.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
library(fs)

test_that("list_deps works", {
test_that("non parsing list_deps works", {

f <- new_factory(path = path_temp(), move_in = FALSE)
on.exit(dir_delete(f))
Expand All @@ -9,13 +9,37 @@ test_that("list_deps works", {
file_copy(path("test_reports", "package_calls.Rmd"), path(f, "report_sources"))
file_copy(path("test_reports", "example.R"), path(f, "report_sources"))

expected_deps_package_calls <- c("purrr", "readxl", "fs", "rmarkdown", "utils", "yaml", "callr", "rprojroot")
# non parsed expected_deps
expected_deps_package_calls_rmd <- c("purrr", "readxl", "fs", "rmarkdown")
expected_deps_example_r <- c("fs", "yaml", "callr", "utils", "rprojroot")
expected_deps_example_report <- c("base", "knitr", "fs")
expected_deps <- c(expected_deps_package_calls_rmd,
expected_deps_example_r,
expected_deps_example_report)

expected_deps_example_report <- c("knitr", "fs", "rmarkdown")
expected_deps_readme <- "rmarkdown"
expected_deps <- c(expected_deps_package_calls,
expected_deps_example_report,
expected_deps_readme)
deps <- list_deps(f)
expect_equal(sort(deps), sort(unique(expected_deps)))
})


test_that("parsing list_deps works", {

f <- new_factory(path = path_temp(), move_in = FALSE)
on.exit(dir_delete(f))

# copy test reports over (as this has inline code)
file_copy(path("test_reports", "package_calls.Rmd"), path(f, "report_sources"))
file_copy(path("test_reports", "example.R"), path(f, "report_sources"))

# non parsed expected_deps
expected_deps_package_calls_rmd <- c("purrr", "readxl", "fs", "rmarkdown")
expected_deps_example_r <- c("fs", "yaml", "callr", "utils", "rprojroot")
expected_deps_example_report <- c("knitr", "fs")
expected_deps <- c(expected_deps_package_calls_rmd,
expected_deps_example_r,
expected_deps_example_report)

deps <- list_deps(f, parse_first = TRUE)
expect_equal(sort(deps), sort(unique(expected_deps)))
})

0 comments on commit 8127f49

Please sign in to comment.