templateFill: Evaluate and fill string templates

View source: R/stringUtils.R

templateFillR Documentation

Evaluate and fill string templates

Description

Given a vector of strings containing {{variables}}, returns a copy replacing the templated fields with the value of the specified variables. Variables must be defined in the calling environment (or the one passed in), or an error will occur. If as.R= TRUE, then any {{R code}} can be used and it will be evaluated to obtain a return value. That is, of course, dangerous if you don't trust the source of your template. All the template code is executed in the same environment, created for this purpose or passed in from the command line. A passed in environment can be used to retrieve variables and functions defined or set in template code.

Usage

templateFill(
  x,
  delim = c("{{", "}}"),
  as.R = FALSE,
  envir = new.env(parent = parent.frame())
)

Arguments

x

Vector of strings with fields containing variables or code to be interpolated.

delim

Vector of two string, the first used to signal the start of a template section and the second used to signal the end. These may not be the same, nor have one embedded in the other. By default the open delimiter is {{ and the close delimiter is }}.

as.R

Set TRUE to allow full R code evaluation. By default is FALSE and only allows variable substitution. Setting this true is a security risk when you don't trust the provider of the template text as much as you trust the person who provided your R code, so it generates a warning.

envir

The execution environment to be used. Can be used to pass in the an environment in which variables are defined for use in interpolation. If not specified, then by default this will be a new environment whose parent is the caller's environment, as returned by parent.frame. Variables visible in the calling function (or set there) will be available for use in the template. Note that although R code will normally only set or change variables in this frame when evaluated, it can set or change variables at any level, hence malicious or careless as.R= TRUE evaluated templates can leak or interfere with other R variables in your code (or indeed in any other package or even system code). With great power comes great responsibility.

Value

A copy of the original vector of strings, but with variable names replaced with their values, or with the result of evaluating the interpolated string as R code. Note that everything is returned as a string, so '{1+1}' is returned as '2'.

Examples

# Template is a single text element (could be multi-line)
templateText <- "Dear {{name}}: Please call me at {{phone}}."
name <- "John Doe"
phone <- "555-555-5555"
templateFill( templateText )
#=> [1] "Dear John Doe: Please call me at 555-555-5555."

# Delimiters can be changed
templateText <- "Dear -<[name]>-: Please contact me at -<[email]>-."
name <- "John"
email <- "the.bobs@layoffs.com"
templateFill( templateText, delim= c( '-<[', ']>-' ))
#=> [1] "Dear John: Please contact me at the.bobs@layoffs.com."

# Multiple text elements (each could be multi line)
templateText <- c( "ID: {{id}}", "Item: {{name}}", "Description: {{desc}}" )
id <- "0001-12"
name <- "widget"
desc <- "Widget to foo the bar."
templateFill( templateText )
#=> [1] "ID: 0001-12"
#=> [2] "Item: widget"
#=> [3] "Description: Widget to foo the bar."

# Evaluating R code
x <- 21
y <- 'Helloooo'
templateText <- c(
    "Simple: {{1 + 1}}",
    "Variables are accessible: {{x *2}}",
    "Complex: {{ echo <- function(x) { paste(x,x,sep='...') }; echo(y) }}",
    "Code environment is shared: {{ echo( 'Goodbyyyy' ) }}"
)
templateFill( templateText, as.R= TRUE )
#=> [1] "Simple: 2"
#=> [2] "Variables are accessible: 42"
#=> [3] "Complex: Helloooo...Helloooo"
#=> [4] "Code environment is shared: Goodbyyyy...Goodbyyyy"
#=> Warning message:
#=> In templateFill(templateText, as.R = TRUE) :
#=>    Potential security risk: templateFill() is evaluating user-provided
#=>    R code If you trust where the template is coming from, you can
#=>    suppress this message with suppressWarnings().

# Using an environment to provide data and to share results back.
env <- new.env()
env$x <- 3
env[['y']] <- 5
templateText <- c(
    "x + y = {{x + y}}",
    "shared z = x*y = {{(z <- x*y)}}",
    "shared function f(x) = x*x = {{f<-function(x) {x*x};f(x)}}"
)
x<-1; y<-2; z<-3 # Ignored as using env
suppressWarnings( templateFill( templateText, as.R= TRUE, envir= env ))
#=> [1] "x + y = 8"
#=> [2] "shared z = x*y = 15"
#=> [3] "shared function f(x) = x*x = 9"
env$z
#=> [1] 15
env$f(3)
#=> [1] 9
x
#=>[1] 1

# Template code CAN affect environment
x <- "safe command"; y <- "also safe command"
templateText<- c(
    "x (template) = {{ x <- 'bad command!!!'; x }}",
    "y (template) = {{ y <<- 'bad command also!!!'; y }}"
)
suppressWarnings( templateFill( templateText, as.R= TRUE ))
#=> [1] "x (template) = bad command!!!"
#=> [2] "y (template) = bad command also!!!"
# Template has reached out and mangled a previously safe variable
paste( "Running", x, sep= " ")
#=> [1] "Running safe command"
paste( "Running", y, sep= " ")
#=> [1] "Running bad command also!!!"


jefferys/JefferysRUtils documentation built on Jan. 12, 2024, 9:18 p.m.