Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit f9ff6cd
Showing
28 changed files
with
1,727 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
Package: mod | ||
Type: Package | ||
Title: Lightweight and Self-Contained Modules for Code Organization | ||
Version: 0.1.3 | ||
Authors@R: c( | ||
person("Siqi", "Zhang", email = "iqis.gnahz@gmail.com", role = c("aut", "cre")), | ||
person() | ||
) | ||
Description: Creates modules inline or from a file. Modules can contain any R object and be nested. Each module have their own scope and package "search path" that does not interfere with one another or the user's working environment. | ||
License: GPL-3 | ||
Encoding: UTF-8 | ||
LazyData: true | ||
RoxygenNote: 6.1.1 | ||
URL: https://github.com/iqis/mod | ||
BugReports: https://github.com/iqis/mod/issues | ||
Suggests: testthat (>= 2.1.0), covr | ||
NeedsCompilation: no | ||
Packaged: 2019-08-22 02:33:34 UTC; Siqi | ||
Author: Siqi Zhang [aut, cre] | ||
Maintainer: Siqi Zhang <iqis.gnahz@gmail.com> | ||
Repository: CRAN | ||
Date/Publication: 2019-08-23 10:40:02 UTC |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
62d2e0dab125947daeeba989c649dbe6 *DESCRIPTION | ||
444b0b6b0039176a81383eb0210f40e7 *NAMESPACE | ||
ebc5efd76f5eac459d48faf9bbd48205 *R/core.R | ||
bd0ef5661acf8a5f8f08d84d9c8fa895 *R/declaratives.R | ||
1078a7dc528d3d50a57208f99bb64977 *R/thing.R | ||
d47f8ea47bbc3fc3c39288e0c27ec189 *R/utils.R | ||
48cfb66aadf8357263ae55fd281fd700 *README.md | ||
d6a497bf5b87a2f9ab0294ddbefb0786 *inst/misc/example_module.R | ||
8a14a3dedd1ab81066c58c6cd515f9e7 *man/as_module.Rd | ||
45806d9f5a5fd7ddc313ec84d4f4088d *man/drop.Rd | ||
e11478952f4dda85707ec048ef8bb31b *man/figures/README-unnamed-chunk-13-1.png | ||
efc5b6da7bc5b98ab29a28a169ded5c1 *man/is_module.Rd | ||
c4cef8aa4559799050111c91298482e6 *man/is_thing.Rd | ||
cf0cacfef3abdfb0ea098621a0414882 *man/module.Rd | ||
3596eea1776ad887fa85a0eb0b68da8a *man/name.Rd | ||
acd1d9b24db7c71169392ecd95156709 *man/print.module.Rd | ||
d93b305eff29d5aa76e3ecfdca2c7f9a *man/provide.Rd | ||
522d1ca661cb811149c903bbbc3d17a9 *man/refer.Rd | ||
3ede6ad91acf15259528036e11748f09 *man/require.Rd | ||
cd1a84fc4ab5dcca948a73e0762f6bc2 *man/sub-.thing.Rd | ||
023032572614c32611cca0cea91ae9cd *man/thing.Rd | ||
8d92630bd288ab2bcd67a64cdd2e9ee3 *man/use.Rd | ||
a2af9fe695a3f32e92d41bc9f77ac84d *tests/testthat.R | ||
f8c43847e18eb7c8844233d211c4b467 *tests/testthat/test-core.R | ||
bb9d3681f726c0b808c4efaf88e12f40 *tests/testthat/test-declaratives.R | ||
3c2e0f6c530562ae73f619426d8676cb *tests/testthat/test-thing.R | ||
f7aceeb1cfa78f41e3257457bd03e41b *tests/testthat/test-utils.R |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Generated by roxygen2: do not edit by hand | ||
|
||
S3method("[",thing) | ||
S3method(print,module) | ||
export(acquire) | ||
export(as_module) | ||
export(drop) | ||
export(is_module) | ||
export(is_thing) | ||
export(module) | ||
export(thing) | ||
export(ule) | ||
export(use) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
#' Make a Module | ||
#' | ||
#' Institute a module object inline or from a file. | ||
#' mod::ule() is a useful shorthand for module() when this package is not attached. | ||
#' | ||
#' @param ... module expression | ||
#' @param module module object, or path to a module file | ||
#' @param parent the enclosing environment | ||
#' @param lock lock the environment; logical | ||
#' @param expose_private expose the private environment as `..private..`; logical | ||
#' | ||
#' @return an \code{environment} of class \code{module} containing defined objects | ||
#' | ||
#' @examples | ||
#' | ||
#' # from file | ||
#' module_path <- system.file("misc", "example_module.R", package = "mod") | ||
#' example_module <- acquire(module_path) | ||
#' | ||
#' example_module$e(123) | ||
#' | ||
#' # inline | ||
#' my_module <- mod::ule({ | ||
#' a <- 1 | ||
#' .a <- 2 | ||
#' f <- function(){.a} | ||
#' }) | ||
#' | ||
#' my_module$a | ||
#' my_module$f | ||
#' | ||
#' @export | ||
#' | ||
module <- function(..., parent = parent.frame(), lock = TRUE, expose_private = FALSE){ | ||
code <- deparse(substitute(...)) | ||
temp_file <- tempfile("inline_module") | ||
write(code, temp_file) | ||
|
||
acquire(temp_file, parent = parent, lock = lock, expose_private = expose_private) | ||
} | ||
|
||
#' @rdname module | ||
#' @export | ||
#' | ||
ule <- module | ||
|
||
|
||
#' @rdname module | ||
#' @export | ||
#' | ||
acquire <- function(module, parent = baseenv(), lock = TRUE, expose_private = FALSE) { | ||
|
||
# if neither from module(), nor already has .R ext, auto suffix with .R | ||
# small .r is forbidden | ||
if (grepl("inline_module", module) | grepl("\\.R$", module)) {} else { | ||
module <- paste0(module, ".R") | ||
} | ||
# make a new environment, private. This envir has everything inside the module | ||
private <- new.env(parent = parent) | ||
|
||
# initialize context signatures | ||
assign("..module..", NULL, envir = private) # an empty signature, for future use | ||
assign("..name..", "", envir = private) # name of module | ||
assign("..path..", module, envir = private) # file path of module | ||
assign("..parent..", parent, envir = private) # specified parent env | ||
assign("..search..", function() search_path_envirs(parent.env(private)), envir = private) # private's search path | ||
assign("..require..", c(), envir = private) # names of packages used in the module | ||
assign("..link..", new.env(parent = parent), envir = private) # an environment that has objects from used packages | ||
assign("..shim..", new.env(parent = private$..link..), envir = private) # bindings from this `mod` package | ||
assign("..mask..", new.env(parent = private$..shim..), envir = private) # bindings that masks functions forbidden in module | ||
parent.env(private) <- private$..mask.. | ||
assign("..provide..", c(), envir = private) # names of provided objects | ||
assign("..refer..", list(), envir = private) # names of referred modules | ||
assign("..public..", new.env(parent = private), envir = private) # public env | ||
|
||
|
||
# inject mask bindings to ..mask.. | ||
mapply(assign, | ||
x = ls(masks), | ||
value = mget(ls(masks), envir = masks), | ||
envir = list(private$..mask..)) | ||
parent.env(private) <- private$..mask.. | ||
|
||
# inject mod package bindings to ..shim.. | ||
mod_ns <- asNamespace("mod") | ||
mapply(assign, | ||
x = ls(mod_ns), | ||
value = mget(ls(mod_ns), envir = mod_ns), | ||
envir = list(private$..shim..)) | ||
|
||
# ..public.. => ..private.. => ..mask.. => ..shim.. => ..link.. => ..parent.. | ||
|
||
# source everything from file to private | ||
sys.source(file = module, envir = private) | ||
|
||
# ====== Do Provide ====== | ||
# provide the variables specified in ..provide.. | ||
# if ..provide.. is empty, provide everything except for `..` prefixed private objs | ||
|
||
# list of objects to be placed in public, from ..provide..; | ||
obj_name_list <- if (length(private$..provide..) != 0) { | ||
private$..provide.. | ||
} else { | ||
ls(private, all.names = TRUE) #This includes hidden objs with name starting w. "." | ||
} | ||
|
||
# Remove "private" objects with name starting w. ".." from list | ||
obj_name_list <- obj_name_list[!grepl("^\\.\\.", obj_name_list)] | ||
|
||
# Assign stuff from obj_list to ..public.. | ||
if (length(obj_name_list) > 0){ | ||
mapply(assign, | ||
x = obj_name_list, | ||
value = mget(obj_name_list, private), | ||
envir = list(private$..public..), | ||
SIMPLIFY = FALSE) | ||
} | ||
# Assign back obj_name_list to ..provide.. | ||
private$..provide.. <- obj_name_list | ||
|
||
|
||
|
||
if (expose_private) assign("..private..", private, envir = private$..public..) | ||
|
||
if (lock) lockEnvironment(private$..public.., bindings = TRUE) | ||
|
||
attr(private$..public.., "name") <- private$..name.. | ||
attr(private$..public.., "path") <- private$..path.. | ||
|
||
class(private$..public..) <- c("module", class(private$..public..)) | ||
|
||
return(private$..public..) | ||
} | ||
|
||
|
||
#' Load/Attach a Module to the Search Path | ||
#' | ||
#' @inheritParams module | ||
#' @param as name when attached to search; character | ||
#' @return \code{TRUE} if successful; invisible | ||
#' | ||
#' @examples | ||
#' | ||
#' module_path <- system.file("misc", "example_module.R", package = "mod") | ||
#' example_module <- acquire(module_path) | ||
#' | ||
#' # Attach module object to search path | ||
#' use(example_module) | ||
#' # or directly from file | ||
#' use(module_path, "example_module") | ||
#' | ||
#' @export | ||
#' | ||
use <- function(module, as, parent = baseenv(), lock = TRUE, expose_private = FALSE){ | ||
if (is_module(module)) { | ||
env <- module | ||
if (missing(as)) as <- deparse(substitute(module)) | ||
} else if (is.character(module) || file.exists(module)) { | ||
env <- acquire(module = module, parent = parent, lock = lock, expose_private = expose_private) | ||
bare_name <- function(path){ | ||
gsub("(\\.+)(?!.*\\1).*$", "", basename(path), perl = TRUE) | ||
} | ||
if (missing(as)) as <- bare_name(module) | ||
} else { | ||
stop("requires module object or path to R file") | ||
} | ||
|
||
name <- paste0("module:",as) | ||
if (name %in% search()) drop(as) | ||
get("attach", envir = .BaseNamespaceEnv, mode = "function")( | ||
what = env, name = name | ||
) | ||
invisible(TRUE) | ||
} | ||
|
||
|
||
#' Test if an Object is a Module | ||
#' | ||
#' @param x An object | ||
#' @return \code{TRUE} if the object is a \code{module}, \code{FALSE} otherwise | ||
#' @export | ||
is_module <- function(x) { | ||
inherits(x, "module") | ||
} | ||
|
||
|
||
#' Print a Module | ||
#' | ||
#' @param x an object | ||
#' @param ... dot-dot-dot, ignored | ||
#' | ||
#' @return the object itself; invisible | ||
#' @export | ||
#' | ||
print.module <- function(x, ...){ | ||
cat("<module>", "\n") | ||
|
||
obj_name_list <- ls(x, all.names = TRUE) | ||
obj_class_list <- lapply(obj_name_list, function(y) class(get(y, envir = x))) | ||
print_line <- function(name, class){ | ||
cat(paste0("- ", name, ": <", paste(class, collapse = ", "), ">\n")) | ||
} | ||
mapply(print_line, name = obj_name_list, class = obj_class_list) | ||
invisible(x) | ||
} | ||
|
||
#' Drop a Module | ||
#' | ||
#' Detach a named module from the search path. If no arguments is supplied, detach the most recently attached module. | ||
#' | ||
#' @param name name of the module to exit from; character | ||
#' @return \code{TRUE} if successful; invisible | ||
#' | ||
#' @examples | ||
#' | ||
#' use(mod::ule({ | ||
#' a <- 1 | ||
#' }), as = "my_module") | ||
#' | ||
#' use(mod::ule({ | ||
#' b <- 2 | ||
#' }), as = "my_other_module") | ||
#' | ||
#' search() | ||
#' | ||
#' # by name | ||
#' drop("my_module") | ||
#' | ||
#' # and at the head position | ||
#' drop() | ||
#' | ||
#' search() | ||
#' | ||
#' @export | ||
#' | ||
drop <- function(name) { | ||
if (missing(name)) { | ||
search_path <- search() | ||
name <- search_path[grepl("module:", search_path)][1] | ||
} else { | ||
name <- paste0("module:", name) | ||
} | ||
|
||
if (is.na(name)) stop("no module attached in search path") | ||
|
||
detach(name = name, character.only = TRUE) | ||
invisible(TRUE) | ||
} | ||
|
Oops, something went wrong.