Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: modules
Title: Self Contained Units of Source Code
Version: 0.8.0
Version: 0.8.1
Authors@R: person("Sebastian", "Warnholz", email = "[email protected]", role = c("aut", "cre"))
Description: Provides modules as an organizational unit for source code. Modules
enforce to be more rigorous when defining dependencies and have
Expand All @@ -24,22 +24,23 @@ Suggests:
lintr,
rmarkdown,
parallel
RoxygenNote: 6.1.0
RoxygenNote: 6.1.1
Collate:
'amodule.R'
'NAMESPACE.R'
'getSearchPath.R'
'amodule.R'
'base-override.R'
'class.R'
'depend.R'
'export.R'
'expose.R'
'extend.R'
'getSearchPath.R'
'import.R'
'module-class.R'
'module-coercion.R'
'module-helper.R'
'module.R'
'use.R'
'module.R'
'module-package.R'
'testModule.R'
'base-override.R'
VignetteBuilder: knitr
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ export(getSearchPathContent)
export(getSearchPathDuplicates)
export(getSearchPathNames)
export(import)
export(initModules)
export(module)
export(use)
export(useModules)
importFrom(utils,download.file)
importFrom(utils,install.packages)
importFrom(utils,installed.packages)
Expand Down
109 changes: 109 additions & 0 deletions R/module-package.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#' Use modules in package build process
#'
#' This function will create a configuration file, such that sub folders inside
#' the R folder of a package are used as module definition. It will then compile
#' all modules.
#'
#' @param folder (character) the folder where to create the configuration file:
#' the package root.
#'
#' @details Guiding principles: (1) this is R CMD check compliant (2) we do not
#' introduce new logic to the semantics of R code.
#'
#' What does it mean to have sub-folders in packages? Each folder defines a
#' module. The name of the module is the name of the folder. Having several R
#' files in a sub-folder adds no additional logic. All files are sourced into
#' the same environment. As with packages, we can make objects public by
#' exporting them. As with packages, nested sub-folders are currently ignored.
#'
#' Each sub-folder is compiled into one module, represented by a regular R file
#' in the package. The file is auto-generated and managed by the build
#' process. The presence of this file enables to take advantage of R CMD
#' check. Hence checks may refer to the target file, however changes need to
#' happen inside the module/sub-folder itself.
#'
#' @rdname initModules
#' @export
useModules <- function(folder = ".") {
addConfigure(folder)
initModules(folder)
}

addConfigure <- function(folder = ".") {
folder <- normalizePath(folder)
configureFile <- normalizePath(paste0(folder, "/configure"))
message(sprintf("Creating %s[.win]", configureFile))
code <- c(
'#!/bin/sh', '',
'$R_HOME/bin/Rscript -e "modules::initModules()"',
'')
writeLines(code, configureFile)
writeLines(code, configureFileWin <- paste0(configureFile, ".win"))
Sys.chmod(configureFile, "0764")
Sys.chmod(configureFileWin, "0764")
}

#' @rdname initModules
#' @export
initModules <- function(folder = ".") {
message("** compiling modules")
folder <- normalizePath(paste0(folder, "/R"), mustWork = FALSE)
removeOutdatedModules(folder)
processModules(folder)
invisible(NULL)
}

removeOutdatedModules <- function(folder) {
subFiles <- getSubFiles(folder)
for (file in subFiles) {
moduleFolder <- sub("module-", "", sub("\\.[rR]$", "", file))
if (!dir.exists(moduleFolder)) {
message(sprintf("*** removing %s: no module folder found", file))
file.remove(file)
}
}
}

processModules <- function(folder) {
subFolders <- getSubFolders(folder)
subFiles <- constSubFiles(folder)
subNames <- getSubNames(folder)
mapply(parseAndWrite, subFolders, subNames, subFiles)
}

parseAndWrite <- function(subFolder, subName, subFile) {
code <- parseModule(subFolder, subName)
if (!file.exists(subFile)) message("*** creating %s", subFile)
writeLines(code, subFile)
}

parseModule <- function(subFolder, subName) {
code <- lapply(list.files(subFolder, "\\.[rR]$", full.names = TRUE), readLines)
code <- unlist(code)
doc <- code[grepl(" *#+'", code)]
code <- code[!grepl(" *#+'", code)]
code <- ifelse(code == "", code, paste0(" ", code))
assignConst <- sprintf("new%s <- function() modules::module({", subName)
assignModule <- sprintf("%s <- new%1$s()", subName)
c(disclaimer(), assignConst, code, "})", "\n", doc, assignModule)
}

disclaimer <- function() "# Generated by modules: do not edit by hand\n"

getSubFolders <- function(folder) {
list.dirs(folder, recursive = FALSE)
}

getSubNames <- function(folder) {
basename(getSubFolders(folder))
}

getSubFiles <- function(folder) {
list.files(folder, "module-.*\\.[rR]$", full.names = TRUE)
}

constSubFiles <- function(folder) {
subNames <- getSubNames(folder)
if (length(subNames) == 0) character(0)
else paste0(folder, "/module-", subNames, ".R")
}
36 changes: 36 additions & 0 deletions man/initModules.Rd

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

3 changes: 3 additions & 0 deletions prepareRepo.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ writeLines(text, "README.md")

## TODO

## - amodule
## - parameters masked by internals
## - dealing with elipses
## - depend
## - on .tar.gz

Expand Down
2 changes: 2 additions & 0 deletions vignettes/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.html
*.R
40 changes: 40 additions & 0 deletions vignettes/modulesInPackages.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: "Modules and subfolders inside R packages"
author: "Sebastian Warnholz"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Modules and subfolders inside R packages}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```

# Introduction

In the following you find how you can use modules and subfolders inside of an R
package. This gives you additional options to impose structure on your
code-base. While using modules inside of packages is a viable option already, it
does not affect the way you structure the files. Also, an R package uses the
sub-folder `R` for all R source code, but ignores all sub-folders within. In
small code bases, this is no problem. Quite the contrary, it's simplicity stops
you from over-complicating things. In large code bases, say everything with
more than 20 files, you may long for something more than a flat folder
structure.

# Example

The idea is very simple. You add a sub-folder to your package. They contain R
source files, just like your ordinary R package.

- Copy & Paste aus dem Paket in einen Unterordner muss funktionieren.
- Umgang mit Dokumentation?
- Umgang mit Exports
- Dokumentation von Modulen
-