#' Make me a Makefile
#'
#' Determine targets and prerequisites based on data files that are read/written
#' in your working directory. Make targets by running scripts.
#'
#' \code{make_file()} will \code{\link[base]{parse}} the R code in 'path' and
#' search for function calls that read/write data files. It uses partial
#' matching, so if \code{read.fun} is set to \code{"read"}, it will match
#' \code{read.delim}, \code{read.table}, \code{readRds} etc.
#'
#' The purpose of this is to extract the values of the \code{file} argument
#' from these calls and create a list of files that are being read/written.
#' (You can actually choose different argument but \code{file} seems like the
#' most sensible default. Partial matching applies here as well so \code{file}
#' and \code{xlsxFile} are both matched by \code{"file"}.)
#'
#' In order for this to work, use named \code{file} argument in your function
#' calls, e.g. \code{load(file = "myfile.RData")} and not
#' \code{load("myfile.RData")}, otherwise the parser will ignore it.
#'
#' Once the list of data files is obtained, a Makefile is created, using files
#' that were written as targets and files that were read as prerequisites.
#' The scripts that do the reading/writing become commands. By default, the
#' shell command for R files is \code{"Rscript \\"myfile.R\\""} and for Rmd
#' files \code{"Rscript -e \\"rmarkdown::render('myfile.Rmd')\\""}.
#'
#' If \code{DRY} is \code{TRUE}, the Makefile is just printed on the console.
#'
#' @param path Directory containing R scripts or a single file
#' @param script.all Which script produces the ultimate target? By default a script that creates targets on which nothing else depends
#' @param read.fun Vector of (partial) function names; e.g. \code{"read"} matches both \code{read.csv} and \code{readRds}.
#' @param write.fun Vector of (partial) function names; e.g. \code{"save"} matches both \code{save} and \code{saveRDS}.
#' @param read.arg Name of argument specifying the file read
#' @param write.arg Name of argument specifying the file written
#' @param script A named list of shell commands for specific file types
#' @param dry Should a Makefile be written (\code{TRUE}) or printed?
#' @importFrom MakefileR make_rule makefile write_makefile
#' @export
make_file = function(path = ".", script.all = NULL,
read.fun = c("read", "load"),
write.fun = c("write", "save"),
read.arg = "[Ff]ile",
write.arg = read.arg,
script = list(
R = "Rscript \"$(<)\"",
Rmd = "Rscript -e \"rmarkdown::render('$(<)')\""),
dry = FALSE) {
parsed = parse_io(
path = path, read.fun = read.fun, write.fun = write.fun,
read.arg = read.arg, write.arg = write.arg, suffix = names(script))
if (!is.null(script.all))
script.all = file.path(path, script.all)
tidy = tidy_io(io.calls = parsed, script = script)
rules = as_Makefiler(io.tidy = tidy, script.all = script.all)
if (dry)
return(makefile(.dots = rules))
else
ask_Makefile(makefile(.dots = rules))
}
ask_Makefile = function(Makefile) {
if (file.exists("Makefile") && as.logical(as.numeric(ask_me())))
write_makefile(Makefile, "Makefile")
else if (file.exists("Makefile"))
message("Aborted")
else
write_makefile(Makefile, "Makefile")
}
ask_me = function(question = "Makefile already exists. Overwrite?",
opts = list(yes = 1, no = 0)) {
message("---")
repeat {
message(question)
for (this.opt in names(opts))
message(paste0(opts[[this.opt]], ": ", this.opt))
ans = readline(prompt="Answer: ")
if (ans %in% unlist(opts))
break
}
ans
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.