A package can import modulr for its own usage. There are then essentially two different and complementary ways to exploit all of the modulr features: for the limited scope of the internals of the package itself and/or for an exported usage.
In this use case, modules are defined and used inside the package’s namespace, without interference with any usage that could be made of modulr outside of the package. Something ressembling the following lines of code should then appear in your ./R
package files:
# File: ./R/init.R
#' @import modulr
NULL
my_package_injector <- get_default_injector()
set_verbosity(0L)
Notice that we are using Roxygen2’s @import
tag here to easily populate the NAMESPACE
file of the package with an appropriate import
field (the @importFrom
tag could also import only a selected subset of exported methods from modulr), so that the prefix modulr::
can be omitted in front of modulr methods (in particular for syntactic sugars like %provides%
and %requires%
, see infra).
It is also important to make sure that this portion of code is loaded before defining and using modules, which is guaranteed by the @include
tag (or by manually specifying a collation order in the NAMESPACE
file), as shown in the following example:
# File: ./R/greetings.R
#' @include init.R
NULL
"hello" %provides% "Hello"
"world" %provides% "World"
"greetings" %requires% list(
hello = "hello",
world = "world"
) %provides% {
paste0(hello, ", ", tolower(world), "!")
}
#' @export
say_hello <- function() {
with_injector(my_package_injector, make("greetings"))
}
Notice the with_injector
wrapper around make("greetings")
in the body of the exported say_hello
function: since say_hello
is suscpetible to be called from outside of the package’s namespace, it is important to specify explicitely that the make
call relies on the package’s default injector captured in the ./R/init.R
file.
It is also possible to use the injector’s get
method:
#' @export
say_hello <- function() {
my_package_injector$get("greetings")
}
Notice that none of the modules is directly exposed by the package:
library(my_package)
(modulr::lsmod())
#> NULL
In this situation, the package is intended to provide the user with a predefined set of modules. The following example shows how to use the .onLoad
hook function to define a module, just before sealing the namespace of the package and processing exports:
# File: ./R/zzz.R
.onLoad <- function(libname, pkgname) {
with_injector(get_default_injector(), {
set_verbosity(0L)
"foobar" %provides% "FOOBAR"
})
invisible(NULL)
}
When the package is loaded, the "foobar"
module is defined within the scope of the default modulr injector situated outside of the package’s namespace. It is then accessible by the user of the package:
library(my_package)
modulr::lsmod()
#> name version storage along type weight calls dependencies uses
#> 1 foobar <NA> in-memory <NA> <NA> <NA> 0 0 0
#> size lines modified
#> 1 152 bytes 2 2018-12-02T16:14:51 UTC
inst/modules
Finally, it is possible to define an entire set of on-disk modules at once in the ./inst/modules
directory. For instance:
# File: ./inst/modules/foo.R
library(modulr)
"foo" %provides% "FOO"
In this case, it is necessary to add the path of the modules
sub-directory of the installed package to the modulr’s root paths. The system.file
function is tailored for this kind of situation:
# File: ./R/zzz.R
.onLoad <- function(libname, pkgname) {
# ...
with_injector(get_default_injector(), {
root_config$set(c(
root_config$get_all()[[1L]],
system.file("modules", package = "my_package")))
})
invisible(NULL)
}
Once the package is loaded, the installed modules
directory is then reachable:
library(my_package)
modulr::make("foo")
#> [2018-12-02T16:14:52 UTC] Resetting modulr state ... OK
#> [2018-12-02T16:14:52 UTC] Defining 'foo' ... OK
#> [2018-12-02T16:14:52 UTC] Making 'foo' ...
#> [2018-12-02T16:14:52 UTC] * Visiting and defining dependencies ...
#> [2018-12-02T16:14:52 UTC] * Constructing dependency graph ... OK
#> [2018-12-02T16:14:52 UTC] DONE ('foo' in 0.018 secs)
#> [1] "FOO"