Skip to content

Commit

Permalink
version 0.1.3
Browse files Browse the repository at this point in the history
  • Loading branch information
iqis authored and cran-robot committed Aug 23, 2019
0 parents commit f9ff6cd
Show file tree
Hide file tree
Showing 28 changed files with 1,727 additions and 0 deletions.
22 changes: 22 additions & 0 deletions DESCRIPTION
@@ -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
27 changes: 27 additions & 0 deletions MD5
@@ -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
13 changes: 13 additions & 0 deletions NAMESPACE
@@ -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)
249 changes: 249 additions & 0 deletions R/core.R
@@ -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)
}

0 comments on commit f9ff6cd

Please sign in to comment.