# A context object for tracking a cache that needs to be dirtied when a set of # files changes on disk. Each time the cache is dirtied, the set of files is # cleared. Therefore, the set of files needs to be re-built each time the cached # code executes. This approach allows for dynamic dependency graphs. CacheContext <- setRefClass( 'CacheContext', fields = list( .dirty = 'logical', .tests = 'list' ), methods = list( initialize = function() { .dirty <<- TRUE # List of functions that return TRUE if dirty .tests <<- list() }, addDependencyFile = function(file) { if (.dirty) return() file <- normalizePath(file) mtime <- file.info(file)$mtime .tests <<- c(.tests, function() { newMtime <- try(file.info(file)$mtime, silent=TRUE) if (is(newMtime, 'try-error')) return(TRUE) return(!identical(mtime, newMtime)) }) invisible() }, forceDirty = function() { .dirty <<- TRUE .tests <<- list() invisible() }, isDirty = function() { if (.dirty) return(TRUE) for (test in .tests) { if (test()) { forceDirty() return(TRUE) } } return(FALSE) }, reset = function() { .dirty <<- FALSE .tests <<- list() }, with = function(func) { oldCC <- .currentCacheContext$cc .currentCacheContext$cc <- .self on.exit(.currentCacheContext$cc <- oldCC) return(func()) } ) ) .currentCacheContext <- new.env() # Indicates to Shiny that the given file path is part of the dependency graph # for whatever is currently executing (so far, only ui.R). By default, ui.R only # gets re-executed when it is detected to have changed; this function allows the # caller to indicate that it should also re-execute if the given file changes. # # If NULL or NA is given as the argument, then ui.R will re-execute next time. dependsOnFile <- function(filepath) { if (is.null(.currentCacheContext$cc)) stop("addFileDependency was called at an unexpected time (no cache context found)") if (is.null(filepath) || is.na(filepath)) .currentCacheContext$cc$forceDirty() else .currentCacheContext$cc$addDependencyFile(filepath) }