Define or redefine a module by name, dependencies, and provider.
define(name, dependencies = NULL, provider = function() NULL)
name | A string (character vector of lenght one). A module name can contain letters, figures and some special characters,
namely Names containing The name "modulr" corresponds to a special module and is therefore reserved. |
---|---|
dependencies | A (preferably named) list of strings. Elements of the list of dependencies designate modules by their name. |
provider | A string, a braced expression, or a function. If any, function's formals must coincide with the list of dependencies. |
A wrapper function around a make call for the defined module.
The definition of a module can be done explicitly in the console, implicitly from a file on a disk, or remotely at a given URL via the HTTP(S) protocol. These three ways of defining modules have their specificities.
This is the most direct method to
define or redefine a module. This is also the most volatile since the
lifespan of the module is limited to the R session. When a new module is
defined, the internal state of the package is modified to record its name,
dependencies and provider. Some other useful metadata are also recorded, like
timestamps, various flags and counters, and a digest. When an existing module
is redefined, the internal state is updated accordingly, unless no change is
detected by digests comparison. No other side-effect occurs during the
definition process, notably the evaluation of the provider which is postponed
to a subsequent make
call.
This is the natural method to choose when a
module is intended to be reused. In such a case, the definition takes place
in a dedicated file, which name is closely related to the module's name.
As a file /home/user/readme.txt
is composed of a path
/home/user
and a file name readme.txt
, a module name
vendor/tool/swissknife
is similarily composed of a namespace
vendor/tool
and a local name swissknife
. For modulr to find
this module, it is sufficient to store its definition in an R, R Markdown or
R Sweave file named swissknife.R[md|nw]
(R files have precedence over
Rmd's and Rnw's), laid out on disk in the vendor/tool
path, relative
to the modulr root directory (see root_config
).
vendor/
tool/
swissknife.R
,
contains the "vendor/tool/swissknife" definition.
The root_config
accessor acts at the
filesystem level, by specifying the root directory, relative to which
all paths are expressed. For instance, root_config$set("./lib")
tells
modulr that all modules are to be found in lib
(in the R working
directory). The directory path can be relative (e.g. ./lib
) or
absolute (e.g. /home/user/lib
). By default, modulr looks in turn into
the following directories "./module"
, "./modules"
,
"./lib"
, "./libs"
, and "."
.
The paths_config
accessor acts at the namespace
level, by mapping a specific namespace to a dedicated path, relative to the
root directory. For instance, paths_config$set("vendor" =
"third_parties/vendor")
will map the vendor/great_module
to the
third_parties/vendor/great_module.R
path, relative to the root
directory.
third_parties
is intended to be a dedicated container for third-parties modules.
vendor
great_module.R
contains the "vendor/great_module" definition.
The
maps_config
accessor acts at the module level, by
substituting specific dependencies within the scope of a given module. This
is especially useful in a situation where a dependency has been replaced by a
newer version, but a module still needs to rely on the previous one. For
instance, maps_config$set("foo/bar" = list("vendor/great_module" =
"vendor/old_great_module"))
tells modulr that for the module foo/bar
only, the dependency vendor/great_module
must be replaced by
vendor/old_great_module
.
foo
bar.R
depends on vendor/great_module
by definition, but will be replaced by
vendor/old_great_module
when needed.
vendor
great_module.R
serves all modules that depend on it, except foo/bar
.
old_great_module.R
serves foo/bar
only.
This is the method used to share a module via the
HTTP(S) protocol. The module is thus served at a given URL and has to be
imported (see import_module
) in order to be defined and used.
Like files, it is possible to store several related definitions at one URL.
Public and private gists, files on GitHub, and any HTTP server can be used to
share so called modulr gears.
name %provides% provider
name %requires% dependencies %provides% provider
It is considered a very bad practice to define, touch, undefine, load, make, reset, or perform any other operation from within a module definition that may alterate the internal state of modulr.
.Last.name
, plot_dependencies
,
import_module
, make
, maps_config
,
paths_config
, reset
, touch
, and
undefine
.
reset()#> [2018-12-02T17:13:57 CET] Resetting modulr state ... OKdefine("foo", NULL, "Hello")#> [2018-12-02T17:13:57 CET] Defining 'foo' ... OKbar <- define("bar", list(foo = "foo"), { paste(foo, "World!") })#> [2018-12-02T17:13:57 CET] Defining 'bar' ... OKbar()#> [2018-12-02T17:13:57 CET] Making 'bar' ... #> [2018-12-02T17:13:57 CET] * Visiting and defining dependencies ... #> [2018-12-02T17:13:57 CET] * Constructing dependency graph ... OK #> [2018-12-02T17:13:57 CET] * Evaluating #1/1 (layer #1/1): 'foo' ... #> [2018-12-02T17:13:57 CET] DONE ('bar' in 0.049 secs)#> [1] "Hello World!"define("foo", NULL, "Again, Hello")#> [2018-12-02T17:13:57 CET] Re-defining 'foo' ... OKbar()#> [2018-12-02T17:13:57 CET] Making 'bar' ... #> [2018-12-02T17:13:57 CET] * Visiting and defining dependencies ... #> [2018-12-02T17:13:57 CET] * Constructing dependency graph ... OK #> [2018-12-02T17:13:57 CET] * Evaluating #1/1 (layer #1/1): 'foo' ... #> [2018-12-02T17:13:57 CET] DONE ('bar' in 0.051 secs)#> [1] "Again, Hello World!"reset()#> [2018-12-02T17:13:57 CET] Resetting modulr state ... OK"foo" %provides% "Hello"#> [2018-12-02T17:13:57 CET] Defining 'foo' ... OK"bar" %requires% list( foo = "foo" ) %provides% { paste(foo, "World!") }#> [2018-12-02T17:13:57 CET] Defining 'bar' ... OKmake()#> [2018-12-02T17:13:57 CET] Making 'bar' ... #> [2018-12-02T17:13:57 CET] * Visiting and defining dependencies ... #> [2018-12-02T17:13:57 CET] * Constructing dependency graph ... OK #> [2018-12-02T17:13:57 CET] * Evaluating #1/1 (layer #1/1): 'foo' ... #> [2018-12-02T17:13:57 CET] DONE ('bar' in 0.049 secs)#> [1] "Hello World!""foo" %provides% "Again, Hello"#> [2018-12-02T17:13:57 CET] Re-defining 'foo' ... OKmake("bar")#> [2018-12-02T17:13:57 CET] Making 'bar' ... #> [2018-12-02T17:13:57 CET] * Visiting and defining dependencies ... #> [2018-12-02T17:13:57 CET] * Constructing dependency graph ... OK #> [2018-12-02T17:13:57 CET] * Evaluating #1/1 (layer #1/1): 'foo' ... #> [2018-12-02T17:13:57 CET] DONE ('bar' in 0.052 secs)#> [1] "Again, Hello World!"reset()#> [2018-12-02T17:13:57 CET] Resetting modulr state ... OKdefine("A", list(b = "B"), function(b) NULL)#> [2018-12-02T17:13:57 CET] Defining 'A' ... OK#> Warning: [2018-12-02T17:13:57 CET] Possibly unused dependency in ‘A’: ‘b’.define("B", list(a = "A"), function(a) NULL)#> [2018-12-02T17:13:57 CET] Defining 'B' ... OK#> Warning: [2018-12-02T17:13:57 CET] Possibly unused dependency in ‘B’: ‘a’.## Not run: make() reset()#> [2018-12-02T17:13:57 CET] Resetting modulr state ... OKdefine("A", NULL, function() NULL)#> [2018-12-02T17:13:57 CET] Defining 'A' ... OKdefine("B", NULL, function() NULL)#> [2018-12-02T17:13:57 CET] Defining 'B' ... OKdefine("C", list(a = "A"), function(a) NULL)#> [2018-12-02T17:13:57 CET] Defining 'C' ... OK#> Warning: [2018-12-02T17:13:57 CET] Possibly unused dependency in ‘C’: ‘a’.define("D", list(a = "A", b = "B"), function(a, b) NULL)#> [2018-12-02T17:13:57 CET] Defining 'D' ... OK#> Warning: [2018-12-02T17:13:57 CET] Possibly 2 unused dependencies in ‘D’: ‘a’, ‘b’.define("E", list(d = "D"), function(d) NULL)#> [2018-12-02T17:13:57 CET] Defining 'E' ... OK#> Warning: [2018-12-02T17:13:57 CET] Possibly unused dependency in ‘E’: ‘d’.define("F", list(c = "C", d = "D", e = "E"), function(c, d, e) NULL)#> [2018-12-02T17:13:57 CET] Defining 'F' ... OK#> Warning: [2018-12-02T17:13:57 CET] Possibly 3 unused dependencies in ‘F’: ‘c’, ‘d’, ‘e’.#> [2018-12-02T17:13:57 CET] Making 'F' ... #> [2018-12-02T17:13:57 CET] * Visiting and defining dependencies ... #> [2018-12-02T17:13:57 CET] * Constructing dependency graph ... OK #> [2018-12-02T17:13:57 CET] * Sorting 5 dependencies with 7 relations ... on 3 layers, OK #> [2018-12-02T17:13:57 CET] * Evaluating new and outdated dependencies ... #> [2018-12-02T17:13:57 CET] ** Evaluating #1/5 (layer #1/3): 'B' ... #> [2018-12-02T17:13:57 CET] ** Evaluating #2/5 (layer #1/3): 'A' ... #> [2018-12-02T17:13:57 CET] ** Evaluating #3/5 (layer #2/3): 'C' ... #> [2018-12-02T17:13:57 CET] ** Evaluating #4/5 (layer #2/3): 'D' ... #> [2018-12-02T17:13:57 CET] ** Evaluating #5/5 (layer #3/3): 'E' ... #> [2018-12-02T17:13:57 CET] DONE ('F' in 0.15 secs)#> NULL