-
Notifications
You must be signed in to change notification settings - Fork 142
R JavaScript binding
An RCloud session runs on both client and server, and it is possible for R functions on the server to call JavaScript functions on the client, and vice versa.
The mechanism used is called an ocap, for Object Capability. In the context of RCloud, this just means a function with its closure, identified by an unguessable hash key. Generally you won't see or care about the value of this hash key, because there are convenience functions which make it look like you are just calling a function.
In the context of writing [RCloud Extensions], the connection between R and JavaScript will start on the R side, with R code requesting JavaScript code to be installed.
Often, this code comes from a resource in an R package; it may also come from an asset in the RCloud notebook:
- Use system.file to get the path to a resource in an R package, and
paste(readLines(f), collapse='\n'))to read the file. - Use
rcloud.get.assetto read the contents of a notebook asset
Then, install the code in the client by calling rcloud.install.js.module, which takes the module name and the source to install.
For example, most RCloud extensions contain a function that looks something like this, which combines loading the code from a package resource, and installing the source on the client:
install.js.resource <- function(package.name, module.path, module.name) {
path <- system.file("javascript", module.path, package=package.name)
caps <- rcloud.install.js.module(module.name,
paste(readLines(path), collapse='\n'))
caps
}
package.name is the name of the package the resource can be found in, usually the current package.
module.path is the filename of the resource within the package.
module.name is a unique key that the javascript "module" will be stored under - it's important to remember that this code is evaluated only once, and can have state, which we'll show in a moment. By convention, this should be a valid JavaScript identifier.
The equivalent for installing JavaScript from an asset is
install.js.asset <- function(asset.name, module.name) {
caps <- rcloud.install.js.module(module.name, rcloud.get.asset(asset.name))
caps
}
asset.name is the name of the asset in the notebook, and module.name is as above.
The format of JavaScript code required by rcloud.install.js.module is particular, and deserves some explanation. Because the code will be evaluated on the client using, yes, eval, the code must be a single expression, without a terminating ;. It must also evaluate to a single function, or an object containing only functions.
Additionally, any JavaScript functions called from R will be called asynchronously: they take a function called a continuation as their last parameter, and only when the continuation is called does execution return to the R function.
Although the continuation does not need to be called immediately, the RCloud compute process will be hung until the continuation is called. As of RCloud 1.3, this means that all of RCloud will be hung, since there is only one process.
The simplest valid JavaScript code that can be installed by RCloud is
(function(x, k) {
k();
})
Which can be installed and called from R like this:
f <- install.js.resource('my.package', 'my.package.js', 'myModule') # or install.js.asset
f('hi')
Or, in object form:
({
f: function(a, k) {
k()
}
})
Which can be installed and called from R like this:
o <- install.js.resource('my.package', 'my.package.js', 'myModule') # or install.js.asset
o$f(1)
To wrap an R function as an ocap so it can be called from JavaScript, call rcloud.support:::make.oc on it, and then pass the result to a JavaScript function that was already wrapped as an ocap.