Consider this presentation of Joe Cheng were he explains how he and his colleagues implemented the reactive framework in shiny (which is inspired by Meteor):
Actual question
Could someone explain to me how I would go about finding out about a reactive object's dependencies (i.e. listing their names and environments, actually accessing them, etc.) that have been automatically inferred by shiny::reactive()
?
More specifically, I'd like to use that information in my custom "one-stop-shop" function setShinyReactive
(package reactr
) that builds on top of shiny functionality.
It must be somehow possible via methods of one of these components/classes:
- a reactive conductor (I'm guessing mainly class
shiny::Observable
) - a reactive endpoints/observers (I'm guessing mainly class
shiny::Observer
) - a reactive context (I'm guessing mainly classes
shiny::Context
andshiny::ReactiveEnvironment
)
But I'm still kind of lost regarding the actual details on this.
Due dilligence
You can find my forked version of shiny that I used for my reverse engineering efforts here. This .Rnw
file represents my current status of knowledge and questions about the actual implementation.
Short example of shiny::reactive()
require(shiny)
## Ensure that shiny let's us do our thing //
shiny_opt <- getOption("shiny.suppressMissingContextError")
if (is.null(shiny_opt) || !shiny_opt) {
options(shiny.suppressMissingContextError = TRUE)
}
a <- 10
shiny::makeReactiveBinding("a")
b <- shiny::reactive(a * 2)
## --> this leads to the inferred result that `b` depends on `a`;
## this information is stored somewhere in the shiny framework
## --> that's what I want to get at
b()
a <- 20
b()
Background: bi-directional bindings
The reason I'd like to be able to do that is that I'd like to be able to specify bi-directional reactive bindings via original (or only slightly adapted) shiny functionality (for examples see this README).
Joe, who has been really helpful and dedicated so far - but who is also short on time, got me started with this:
require(shiny)
## Ensure that shiny let's us do our thing //
shiny_opt <- getOption("shiny.suppressMissingContextError")
if (is.null(shiny_opt) || !shiny_opt) {
options(shiny.suppressMissingContextError = TRUE)
}
suppressWarnings(rm(a, b))
options(shiny.suppressMissingContextError=TRUE)
makeReactiveBinding("a")
makeReactiveBinding("b")
observe(a <<- b)
observe(b <<- a)
shiny:::setAutoflush(TRUE)
(a <- 1)
b
(b <- 2)
a
(a <- 3)
b
However, in order to integrate that into my implementation of setShinyReactive
, I need to figure out if the statement o$.value <<- v
(currently line 203) is valid or not:
## Call to 'makeActiveBinding' //
makeActiveBinding(
id,
env = where,
fun = local({
visible
o
function(v) {
if (missing(v)) {
} else {
if (strict_set == 0) {
o$.value <<- v
## --> this should only be allowed for bi-directional relationships
} else if (strict_set == 1) {
[...]
} else if (strict_set == 2) {
[...]
}
}
[...]
}
})
)
This is only the case for bi-directional reactive objects as they are sort of "hybrids": both reactive conductors and reactive sources at the same time.
That's why I need to figure out if a bi-directional situations exists or not - which in turn implies that I need to find out each objects dependencies
For my other, sort of "pedestrian", take on reactivity based on setReactive
, I queried bi-directional dependencies via method .hasBidirectional
. That's the information that I'd like to retrieve from the correct shiny instance/class that actually holds this information somewhere.