Question

For a given reference class method, how can I determine if it is inherited? More generally, how to determine how far up the inheritance tree I am?

For example, if my setup is:

A <- setRefClass("A",
        methods = list(foo = function() whosMethod())
    )
B <- setRefClass("B",
        contains = "A",
        methods = list(bar = function() whosMethod())
    )
b <- B()

ideally, I want whosMethod() to give me something like

> b$foo()
[1] "A"         # or maybe a numeric value like: [1] 1L

> b$bar()
[1] "B"         # or maybe a numeric value like: [1] 0L

Note this is distinctly different from class(.self), which would always return "B" in the above example.

Motivation - custom events

I want to have inheritance-like behavior for other things besides methods, for instance custom events. My methods may raise(someEvent) and during instantiation I pass event handlers to handle those events, e.g.

MyDatabase <- setRefClass(....)
datasourceA <- MyDatabase(....,
    eventHandlers = list(
        someEvent = function() message("Hello from myObj!"),
        beforeInsert = function(data) {
            if (!dataIsValid(data))
                stop("Data is not valid!")
        }
    )
)

Now, if a child class defines a event handler that has already been defined by a parent class, then I need to know which event handler should be overridden. In particular, if a methodA() registers handlerA() for someEvent and methodB() in a child class registers handlerB() for the same event, when attempting to register handlerA() in methodA() I need to know that I am in an parent method so that if handlerB() is already registered, I do not override it.

It would also be nice to be able to call parent event handlers from child ones, like callSuper() available to methods.

Was it helpful?

Solution

Try this:

Traverse the inheritance graph and get respective methods

methodsPerClass <- function(x) {
    if (!inherits(x, "envRefClass")) {
        stop("This only works for Reference Class objects")
    }
    ## Get all superclasses of class of 'x' //
    supercl <- selectSuperClasses(getClass(class(b)), directOnly=FALSE)
    ## Get all methods per superclass //
    out <- lapply(c(class(x), supercl), function(ii) {
        ## Get generator object //
        generator <- NULL
        if (inherits(getClass(ii), "refClassRepresentation")) {
            generator <- getRefClass(ii)
        }
        ## Look up method names in class defs //
        out <- NULL
        if (!is.null(generator)) {
            out <- names(Filter(function(x) {
                    attr(x, "refClassName") == generator$className
                }, 
                as.list(generator$def@refMethods))
            )        
        }
        return(out)
    })
    names(out) <- supercl
    ## Filter out the non-reference-classes //
    idx <- which(sapply(out, is.null))
    if (length(idx)) {
        out <- out[-idx]
    }
    ## Nicer name for actual class of 'x' //
    idx <- which(names(out) == "envRefClass") 
    if (length(idx)) {
        names(out)[idx] <- class(x)
    }
    return(out)
}

I'm sure that one could come up with some nicer way of filtering out non-reference-classes in order to get rid of the 'idx' parts at the end, but it works.

This would give you:

methodsPerClass(x=b)
$A
[1] "bar"

$B
[1] "foo"

$.environment
 [1] "import"       "usingMethods" "show"         "getClass"     "untrace"     
 [6] "export"       "callSuper"    "copy"         "initFields"   "getRefClass" 
[11] "trace"        "field"     

Query which method(s) belong to which class in particular

whosMethod <- function(x, method) {
    mthds <- methodsPerClass(x=x)
    out <- lapply(method, function(m) {
        pattern <- paste0("^", m, "$")
        idx <- which(sapply(mthds, function(ii) {
            any(grepl(pattern, ii))
        }))
        if (!length(idx)) {
            stop(paste0("Invalid method '", m, 
                "' (not a method of class '", class(x), "')"))
        }
        out <- names(idx)
    })
    names(out) <- method
    return(out)
}

This would give you:

whosMethod(x=b, method="foo")
$foo
[1] "B"

whosMethod(x=b, method=c("foo", "bar"))
$foo
[1] "B"

$bar
[1] "A"

whosMethod(x=b, method=c("foo", "bar", "nonexisting"))
Error in FUN(c("foo", "bar", "nonexisting")[[3L]], ...) : 
  Invalid method 'nonexisting' (not a method of class 'B')

Run it for all methods of class 'B':

whosMethod(x=b, method=unlist(methodsPerClass(x=b)))
$bar
[1] "A"

$foo
[1] "B"

$import
[1] ".environment"

$usingMethods
[1] ".environment"

$show
[1] ".environment"

$getClass
[1] ".environment"

$untrace
[1] ".environment"

$export
[1] ".environment"

$callSuper
[1] ".environment"

$copy
[1] ".environment"

$initFields
[1] ".environment"

$getRefClass
[1] ".environment"

$trace
[1] ".environment"

$field
[1] ".environment"
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top