-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Flexible default args and cache reset
- Flexible default arguments in 'cafun_create' - Reset of internal cache ('cafun_reset_cache') - Added BACKLOG.md - Added examples - Refined tests
- Loading branch information
Janko Thyson
committed
Feb 4, 2018
1 parent
9a1f421
commit 8b1a4c5
Showing
12 changed files
with
345 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# cachefun 0.1.0 | ||
|
||
* Carefully think about default setting of `refresh`. | ||
|
||
Really depends on the most frequent use case: avoid unnecessary re-executions of long-running functions (probably mostly linked to data I/O and data wrangling) or avoid confusion through forgetting to refresh cached results. | ||
|
||
It's probably best to go with `refresh = TRUE` here. | ||
|
||
* Is context information via `.verbose` really that relevant? | ||
|
||
Adds clarity, but hurts performance and I don't like the current implementation of `.verbose` in the `shiny::reactive`. | ||
|
||
* Find out what's best practice regarding setting defaults arg values in inner function returned by `cafun_create` | ||
|
||
* Understand **where** the cached information is actually stored and how it can be purged (to allow explicit purges/removals like via `rm`) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,7 @@ | ||
exportPattern("^[[:alpha:]]+") | ||
# Generated by roxygen2: do not edit by hand | ||
|
||
export(cafun_create) | ||
export(cafun_reset_cache) | ||
importFrom(shiny,isolate) | ||
importFrom(shiny,reactive) | ||
importFrom(shiny,reactiveValues) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
# cachefun 0.1.0 | ||
|
||
* Added a `NEWS.md` file to track changes to the package. | ||
* Initial version | ||
|
||
# cachefun 0.1.1 | ||
|
||
* Variable default settings | ||
* Reset of internal cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
|
||
# Aux --------------------------------------------------------------------- | ||
|
||
get_env_to_assign_to <- function( | ||
use_env = c("temp", "global", "package") | ||
) { | ||
# nsName <- "temp" | ||
# (ns <- asNamespace(nsName)) # <environment: namespace:stats> | ||
# Keep as reference | ||
|
||
# as.environment( "package:dlaker") | ||
# Keep as reference | ||
|
||
# getPackageName(2) | ||
# Get name that corresponds to second entry on the search list | ||
# Keep as reference | ||
|
||
use_env <- match.arg(use_env) | ||
|
||
switch(use_env, | ||
"temp" = as.environment("._dlaker_temp"), | ||
"global" = .GlobalEnv, | ||
# "package" = as.environment(search()[2]) | ||
|
||
# TODO-20180131-2: | ||
# This seems VERY fragile/dangerous and probably only | ||
# makes sense in context where the datacon generics/methods should be part | ||
# of a PACKAGE that the developer is building. Not sure what the best | ||
# option would be in use cases where a developer would work in a PROJECT | ||
# setting (i.e. only applying packages to solve actual tasks) | ||
|
||
"package" = as.environment(sprintf("package:%s", devtools::as.package("."))) | ||
# TODO-20180131-2-SOLVED: | ||
# This seems to be much more robust regarding the package's namespace | ||
# environment position on the search path | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# Internal cache factory -------------------------------------------------- | ||
|
||
#' Create cache-aware function | ||
#' @importFrom shiny reactiveValues | ||
#' @importFrom shiny reactive | ||
#' @importFrom shiny isolate | ||
#' @example inst/examples/ex-cafun_main.R | ||
#' @export | ||
cafun_create <- function( | ||
fun = NULL, | ||
.refresh = FALSE, | ||
.verbose = FALSE | ||
|
||
) { | ||
reactv <- shiny::reactiveValues() | ||
dat_reactive <- shiny::reactive({ | ||
if(reactv$.verbose) message('Caching result...') | ||
reactv$data | ||
}) | ||
|
||
cafun <- function( | ||
fun, | ||
refresh = TRUE, | ||
reset = FALSE, | ||
.verbose = FALSE, | ||
... | ||
) { | ||
# Reset ----- | ||
if (reset) { | ||
if (.verbose) message(shiny::isolate(object.size(dat_reactive()))) | ||
# rm(data, envir = reactv) | ||
reactv$data <<- NULL | ||
if (.verbose) message(shiny::isolate(object.size(dat_reactive()))) | ||
return(invisible(NULL)) | ||
} | ||
|
||
# Transfer settings ----- | ||
reactv$.verbose <<- .verbose | ||
|
||
if (shiny::isolate(is.null(reactv$data)) | refresh) { | ||
fun_res <- fun(...) | ||
# fun_res <- rlang::eval_tidy(fun()) | ||
reactv$data <<- fun_res | ||
} | ||
|
||
# Relay cache-handling to shiny ----- | ||
shiny::isolate(dat_reactive()) | ||
} | ||
|
||
# Transfer default values ----- | ||
|
||
# TODO-20180204-1: | ||
# This seems too inolved >> find better solution | ||
|
||
.formals <- formals(cafun) | ||
if (!is.null(fun)) .formals$fun <- fun | ||
.formals$refresh <- .refresh | ||
.formals$.verbose <- .verbose | ||
formals(cafun) <- .formals | ||
|
||
cafun | ||
} | ||
|
||
# Reset cache ------------------------------------------------------------- | ||
|
||
#' Reset internal cache of cache-aware function | ||
#' @example inst/examples/ex-cafun_main.R | ||
#' @export | ||
cafun_reset_cache <- function(cafun, .verbose = FALSE) { | ||
cafun(reset = TRUE, .verbose = .verbose) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Define a regular function that you'd like to make "cache-aware" | ||
fun <- function() Sys.time() | ||
|
||
# Turn this function into a cache-aware function | ||
cafun <- cafun_create(fun = fun) | ||
|
||
str(cafun) | ||
# Note that default value for arg 'refresh' >> you need to explicitly refresh | ||
|
||
cafun() # Initial execution of your inner/actual function 'fun' | ||
cafun() # Subsequent execution: internally cached result of 'fun' is returned | ||
cafun(refresh = TRUE) # Explicit refresh request: 'fun' is re-executed and | ||
# new result is cached internally | ||
cafun() # Subsequent execution: internally cached result of 'fun' is returned | ||
|
||
# ----- | ||
|
||
# Change the default value of args | ||
cafun <- cafun_create(fun = fun, .refresh = TRUE) | ||
|
||
str(cafun) | ||
# Note that default value for arg 'refresh' is not 'FALSE' >> you don't need to | ||
# explicitly refresh the internal cache. However, now you must explictly state | ||
# when you **don't** want to refresh the cache - or to put in other words - when | ||
# you want to make use of the internal cache | ||
|
||
cafun() # Inner function executed | ||
cafun() # Inner function re-executed | ||
cafun(refresh = FALSE) # Internal cache value returned | ||
cafun(refresh = FALSE) # Internal cache value returned | ||
cafun() # Inner function re-executed | ||
|
||
# ----- | ||
|
||
# Inner function with argumeents | ||
fun <- function(x) Sys.time() + x | ||
|
||
cafun <- cafun_create(fun = fun) | ||
|
||
cafun(x = 3600) # Inner function executed | ||
cafun(x = 3600 * 5) # Internal cache value returned | ||
cafun(x = 3600 * 5, refresh = TRUE) # Inner function re-executed | ||
|
||
# ----- | ||
|
||
# Reset internal cache |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
library(testthat) | ||
library(cachefun) | ||
|
||
test_check("cachefun") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
library(testthat) | ||
context("Test cache-aware functions") | ||
|
||
test_that("20180204-1: cache-aware function: initial", { | ||
expect_is(fun_with_cache <- cafun_create(), "function") | ||
|
||
fun <- function() "hello world!" | ||
expectation <- "hello world!" | ||
expect_identical(fun_with_cache(fun = fun), expectation) | ||
expect_identical(fun_with_cache(fun = fun), expectation) | ||
|
||
fun <- function() "hello WORLD!" | ||
expectation <- "hello WORLD!" | ||
expect_identical(fun_with_cache(fun = fun, refresh = TRUE), expectation) | ||
expect_identical(fun_with_cache(fun = fun), expectation) | ||
}) | ||
|
||
test_that("20180204-2: cache-aware function: verbose", { | ||
expect_is(fun_with_cache <- cafun_create(), "function") | ||
|
||
fun <- function() "hello world!" | ||
expectation <- "hello world!" | ||
expect_message( | ||
expect_identical(fun_with_cache(fun = fun, .verbose = TRUE), expectation), | ||
"Caching result" | ||
) | ||
}) | ||
|
||
test_that("20180204-3: cache-aware function: reset cache", { | ||
# Inner function with argumeents | ||
fun <- function(x) rnorm(x) | ||
|
||
cafun <- cafun_create(fun = fun) | ||
|
||
res <- cafun(x = 3600) | ||
|
||
cafun_reset_cache(cafun = cafun, .verbose = TRUE) | ||
}) |