-->

Retrieving reactive dependencies as inferred by sh

2020-03-25 09:34发布

问题:

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:

  1. a reactive conductor (I'm guessing mainly class shiny::Observable)
  2. a reactive endpoints/observers (I'm guessing mainly class shiny::Observer)
  3. a reactive context (I'm guessing mainly classes shiny::Context and shiny::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.