简体   繁体   中英

How to let function defined in global environment access variables defined in its calling function's environment?

connect_dw <- function() {
  DBI::dbConnect(RSQLite::SQLite(), ":memory:")
}

fetch_sql_res <- function(key, ...) {
  query_list <- list(...)
  query_sql_res <- function(.query_list = query_list, .db_connect_f = connect_dw) {
    con <- .db_connect_f()
    res <- purrr::map(.query_list, glue::glue_sql, .con = con ) %>%
      purrr::map(purrr::partial(DBI::dbGetQuery, conn = con))
    DBI::dbDisconnect(con)
    res
  }

 ## if key exists in redis cache, fetch it from redis.
 ## If not, call the function to query database

  query_sql_res()

}

#' testing
#' @serializer unboxedJSON
#' @get /test_db
test_db <- function() {
  userid <- 10

  fetch_sql_res(df = "SELECT {userid}")
}

Error message:

 Error in eval(parse(text = text, keep.source = FALSE), envir) : 
  object 'userid' not found

I want to create a function that can be used inside a function (which is used to create web api with plumber).

The function is used to fetch sql result. It needs some variable to construct the sql query.

The above code doesn't work. It's seems the problem is about environments.

All R functions are lexically scoped, which means they inherit variables from where the function is defined, not where it is called. You can't change this default behavior because it would break a lot of things. So you just need to be careful with specifying the correct environment when you are evaulating function calls that need different environments.

In this case the issues seems to be with glue::glue_sql not being able to find the variable. That function has a .envir= parameter that tells it where to look for variables. You just need to explictly pass the calling environment to that function. For example

fetch_sql_res <- function(key, ...) {
  query_list <- list(...)
  calling_env <- parent.frame()
  query_sql_res <- function(.query_list = query_list, .db_connect_f = connect_dw) {
    con <- .db_connect_f()
    res <- purrr::map(.query_list, glue::glue_sql, .con = con, .envir=calling_env ) %>%
      purrr::map(purrr::partial(DBI::dbGetQuery, conn = con))
    DBI::dbDisconnect(con)
    res
  }
  query_sql_res()
}

This isn't a "general purpose" solution but the same basic idea would apply in other places. You need to explicitly refer to the calling environment when you need it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM