glue/ 0000755 0001762 0000144 00000000000 13442031352 011203 5 ustar ligges users glue/inst/ 0000755 0001762 0000144 00000000000 13441546415 012172 5 ustar ligges users glue/inst/doc/ 0000755 0001762 0000144 00000000000 13441546415 012737 5 ustar ligges users glue/inst/doc/speed.R 0000644 0001762 0000144 00000003033 13441546415 014161 0 ustar ligges users ## ----setup, include = FALSE----------------------------------------------
knitr::opts_chunk$set(
collapse = TRUE, comment = "#>",
eval = as.logical(Sys.getenv("EVAL_VIGNETTES", "FALSE")),
cache = FALSE)
library(glue)
## ----setup2, include = FALSE---------------------------------------------
plot_comparison <- function(x, ...) {
library(ggplot2)
library(microbenchmark)
x$expr <- forcats::fct_reorder(x$expr, x$time)
colors <- ifelse(levels(x$expr) == "glue", "orange", "grey")
autoplot(x, ...) +
theme(axis.text.y = element_text(color = colors)) +
aes(fill = expr) + scale_fill_manual(values = colors, guide = FALSE)
}
## ---- message = FALSE----------------------------------------------------
bar <- "baz"
simple <-
microbenchmark::microbenchmark(
glue = glue::glue("foo{bar}"),
gstring = R.utils::gstring("foo${bar}"),
paste0 = paste0("foo", bar),
sprintf = sprintf("foo%s", bar),
str_interp = stringr::str_interp("foo${bar}"),
rprintf = rprintf::rprintf("foo$bar", bar = bar)
)
print(unit = "eps", order = "median", signif = 4, simple)
plot_comparison(simple)
## ---- message = FALSE----------------------------------------------------
bar <- rep("bar", 1e5)
vectorized <-
microbenchmark::microbenchmark(
glue = glue::glue("foo{bar}"),
gstring = R.utils::gstring("foo${bar}"),
paste0 = paste0("foo", bar),
sprintf = sprintf("foo%s", bar),
rprintf = rprintf::rprintf("foo$bar", bar = bar)
)
print(unit = "ms", order = "median", signif = 4, vectorized)
plot_comparison(vectorized, log = FALSE)
glue/inst/doc/transformers.html 0000644 0001762 0000144 00000060353 13441546415 016361 0 ustar ligges users
Transformers
Transformers
Transformers allow you to apply functions to the glue input and output, before and after evaluation. This allows you to write things like glue_sql()
, which automatically quotes variables for you or add a syntax for automatically collapsing outputs.
The transformer functions simply take two arguments text
and envir
, where text
is the unparsed string inside the glue block and envir
is the execution environment. Most transformers will then call eval(parse(text = text, keep.source = FALSE), envir)
which parses and evaluates the code.
You can then supply the transformer function to glue with the .transformer
argument. In this way users can define manipulate the text before parsing and change the output after evaluation.
It is often useful to write a glue()
wrapper function which supplies a .transformer
to glue()
or glue_data()
and potentially has additional arguments. One important consideration when doing this is to include .envir = parent.frame()
in the wrapper to ensure the evaluation environment is correct.
Some examples implementations of potentially useful transformers follow. The aim right now is not to include most of these custom functions within the glue
package. Rather users are encouraged to create custom functions using transformers to fit their individual needs.
glue/inst/doc/speed.Rmd 0000644 0001762 0000144 00000007056 13413432727 014512 0 ustar ligges users ---
title: "Speed of glue"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Speed of glue}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
% \VignetteDepends{R.utils
R.utils,
forcats,
microbenchmark,
rprintf,
stringr,
ggplot2}
---
Glue is advertised as
> Fast, dependency free string literals
So what do we mean when we say that glue is fast. This does not mean glue is
the fastest thing to use in all cases, however for the features it provides we
can confidently say it is fast.
A good way to determine this is to compare it's speed of execution to some alternatives.
- `base::paste0()`, `base::sprintf()` - Functions in base R implemented in C
that provide variable insertion (but not interpolation).
- `R.utils::gstring()`, `stringr::str_interp()` - Provides a similar interface
as glue, but using `${}` to delimit blocks to interpolate.
- `pystr::pystr_format()`[^1], `rprintf::rprintf()` - Provide a interfaces similar
to python string formatters with variable replacement, but not arbitrary
interpolation.
```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE, comment = "#>",
eval = as.logical(Sys.getenv("EVAL_VIGNETTES", "FALSE")),
cache = FALSE)
library(glue)
```
```{r setup2, include = FALSE}
plot_comparison <- function(x, ...) {
library(ggplot2)
library(microbenchmark)
x$expr <- forcats::fct_reorder(x$expr, x$time)
colors <- ifelse(levels(x$expr) == "glue", "orange", "grey")
autoplot(x, ...) +
theme(axis.text.y = element_text(color = colors)) +
aes(fill = expr) + scale_fill_manual(values = colors, guide = FALSE)
}
```
## Simple concatenation
```{r, message = FALSE}
bar <- "baz"
simple <-
microbenchmark::microbenchmark(
glue = glue::glue("foo{bar}"),
gstring = R.utils::gstring("foo${bar}"),
paste0 = paste0("foo", bar),
sprintf = sprintf("foo%s", bar),
str_interp = stringr::str_interp("foo${bar}"),
rprintf = rprintf::rprintf("foo$bar", bar = bar)
)
print(unit = "eps", order = "median", signif = 4, simple)
plot_comparison(simple)
```
While `glue()` is slower than `paste0`,`sprintf()` it is
twice as fast as `str_interp()` and `gstring()`, and on par with `rprintf()`.
`paste0()`, `sprintf()` don't do string interpolation and will likely always be
significantly faster than glue, glue was never meant to be a direct replacement
for them.
`rprintf()` does only variable interpolation, not arbitrary expressions, which
was one of the explicit goals of writing glue.
So glue is ~2x as fast as the two functions (`str_interp()`, `gstring()`) which do have
roughly equivalent functionality.
It also is still quite fast, with over 6000 evaluations per second on this machine.
## Vectorized performance
Taking advantage of glue's vectorization is the best way to avoid performance.
For instance the vectorized form of the previous benchmark is able to generate
100,000 strings in only 22ms with performance much closer to that of
`paste0()` and `sprintf()`. NB. `str_interp()` does not support
vectorization, so were removed.
```{r, message = FALSE}
bar <- rep("bar", 1e5)
vectorized <-
microbenchmark::microbenchmark(
glue = glue::glue("foo{bar}"),
gstring = R.utils::gstring("foo${bar}"),
paste0 = paste0("foo", bar),
sprintf = sprintf("foo%s", bar),
rprintf = rprintf::rprintf("foo$bar", bar = bar)
)
print(unit = "ms", order = "median", signif = 4, vectorized)
plot_comparison(vectorized, log = FALSE)
```
[^1]: pystr is no longer available from CRAN due to failure to correct
installation errors and was therefore removed from further testing.
glue/inst/doc/speed.html 0000644 0001762 0000144 00000160521 13441546415 014732 0 ustar ligges users
Speed of glue
Speed of glue
Glue is advertised as
Fast, dependency free string literals
So what do we mean when we say that glue is fast. This does not mean glue is the fastest thing to use in all cases, however for the features it provides we can confidently say it is fast.
A good way to determine this is to compare it’s speed of execution to some alternatives.
base::paste0()
, base::sprintf()
- Functions in base R implemented in C that provide variable insertion (but not interpolation).
R.utils::gstring()
, stringr::str_interp()
- Provides a similar interface as glue, but using ${}
to delimit blocks to interpolate.
pystr::pystr_format()
, rprintf::rprintf()
- Provide a interfaces similar to python string formatters with variable replacement, but not arbitrary interpolation.
Simple concatenation
bar <- "baz"
simple <-
microbenchmark::microbenchmark(
glue = glue::glue("foo{bar}"),
gstring = R.utils::gstring("foo${bar}"),
paste0 = paste0("foo", bar),
sprintf = sprintf("foo%s", bar),
str_interp = stringr::str_interp("foo${bar}"),
rprintf = rprintf::rprintf("foo$bar", bar = bar)
)
print(unit = "eps", order = "median", signif = 4, simple)
#> Unit: evaluations per second
#> expr min lq mean median uq max neval cld
#> rprintf 265.70 1848 1935 1957 2129 2331 100 a
#> gstring 19.56 2189 2312 2358 2522 2885 100 a
#> str_interp 203.40 2783 3060 3050 3549 3845 100 a
#> glue 476.60 5028 5392 5498 6154 7384 100 a
#> sprintf 53900.00 411900 579900 501300 599000 1534000 100 b
#> paste0 111000.00 312700 527000 535700 633300 1065000 100 b
plot_comparison(simple)

While glue()
is slower than paste0
,sprintf()
it is twice as fast as str_interp()
and gstring()
, and on par with rprintf()
.
paste0()
, sprintf()
don’t do string interpolation and will likely always be significantly faster than glue, glue was never meant to be a direct replacement for them.
rprintf()
does only variable interpolation, not arbitrary expressions, which was one of the explicit goals of writing glue.
So glue is ~2x as fast as the two functions (str_interp()
, gstring()
) which do have roughly equivalent functionality.
It also is still quite fast, with over 6000 evaluations per second on this machine.
glue/inst/doc/transformers.Rmd 0000644 0001762 0000144 00000011201 13413432727 016122 0 ustar ligges users ---
title: "Transformers"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Transformers}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
Transformers allow you to apply functions to the glue input and output, before
and after evaluation. This allows you to write things like `glue_sql()`, which
automatically quotes variables for you or add a syntax for automatically
collapsing outputs.
The transformer functions simply take two arguments `text` and `envir`, where
`text` is the unparsed string inside the glue block and `envir` is the
execution environment. Most transformers will then call `eval(parse(text = text,
keep.source = FALSE), envir)` which parses and evaluates the code.
You can then supply the transformer function to glue with the `.transformer`
argument. In this way users can define manipulate the text before parsing and
change the output after evaluation.
It is often useful to write a `glue()` wrapper function which supplies a
`.transformer` to `glue()` or `glue_data()` and potentially has additional
arguments. One important consideration when doing this is to include
`.envir = parent.frame()` in the wrapper to ensure the evaluation environment
is correct.
Some examples implementations of potentially useful transformers follow. The
aim right now is not to include most of these custom functions within the
`glue` package. Rather users are encouraged to create custom functions using
transformers to fit their individual needs.
```{r, include = FALSE}
library(glue)
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```
### collapse transformer
A transformer which automatically collapses any glue block ending with `*`.
```{r}
collapse_transformer <- function(regex = "[*]$", ...) {
function(text, envir) {
if (grepl(regex, text)) {
text <- sub(regex, "", text)
}
res <- eval(parse(text = text, keep.source = FALSE), envir)
glue_collapse(res, ...)
}
}
glue("{1:5*}\n{letters[1:5]*}", .transformer = collapse_transformer(sep = ", "))
glue("{1:5*}\n{letters[1:5]*}", .transformer = collapse_transformer(sep = ", ", last = " and "))
```
### Shell quoting transformer
A transformer which automatically quotes variables for use in shell commands,
e.g. via `system()` or `system2()`.
```{r}
shell_transformer <- function(type = c("sh", "csh", "cmd", "cmd2")) {
type <- match.arg(type)
function(text, envir) {
res <- eval(parse(text = text, keep.source = FALSE), envir)
shQuote(res)
}
}
glue_sh <- function(..., .envir = parent.frame(), .type = c("sh", "csh", "cmd", "cmd2")) {
.type <- match.arg(.type)
glue(..., .envir = .envir, .transformer = shell_transformer(.type))
}
filename <- "test"
writeLines(con = filename, "hello!")
command <- glue_sh("cat {filename}")
command
system(command)
```
### emoji transformer
A transformer which converts the text to the equivalent emoji.
```{r, eval = require("emo")}
emoji_transformer <- function(text, envir) {
if (grepl("[*]$", text)) {
text <- sub("[*]$", "", text)
glue_collapse(ji_find(text)$emoji)
} else {
ji(text)
}
}
glue_ji <- function(..., .envir = parent.frame()) {
glue(..., .open = ":", .close = ":", .envir = .envir, .transformer = emoji_transformer)
}
glue_ji("one :heart:")
glue_ji("many :heart*:")
```
### sprintf transformer
A transformer which allows succinct sprintf format strings.
```{r}
sprintf_transformer <- function(text, envir) {
m <- regexpr(":.+$", text)
if (m != -1) {
format <- substring(regmatches(text, m), 2)
regmatches(text, m) <- ""
res <- eval(parse(text = text, keep.source = FALSE), envir)
do.call(sprintf, list(glue("%{format}f"), res))
} else {
eval(parse(text = text, keep.source = FALSE), envir)
}
}
glue_fmt <- function(..., .envir = parent.frame()) {
glue(..., .transformer = sprintf_transformer, .envir = .envir)
}
glue_fmt("π = {pi:.2}")
```
### safely transformer
A transformer that acts like `purrr::safely()`, which returns a value instead of an error.
```{r}
safely_transformer <- function(otherwise = NA) {
function(text, envir) {
tryCatch(
eval(parse(text = text, keep.source = FALSE), envir),
error = function(e) if (is.language(otherwise)) eval(otherwise) else otherwise)
}
}
glue_safely <- function(..., .otherwise = NA, .envir = parent.frame()) {
glue(..., .transformer = safely_transformer(.otherwise), .envir = .envir)
}
# Default returns missing if there is an error
glue_safely("foo: {xyz}")
# Or an empty string
glue_safely("foo: {xyz}", .otherwise = "Error")
# Or output the error message in red
library(crayon)
glue_safely("foo: {xyz}", .otherwise = quote(glue("{red}Error: {conditionMessage(e)}{reset}")))
```
glue/inst/doc/transformers.R 0000644 0001762 0000144 00000005751 13441546415 015617 0 ustar ligges users ## ---- include = FALSE----------------------------------------------------
library(glue)
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
## ------------------------------------------------------------------------
collapse_transformer <- function(regex = "[*]$", ...) {
function(text, envir) {
if (grepl(regex, text)) {
text <- sub(regex, "", text)
}
res <- eval(parse(text = text, keep.source = FALSE), envir)
glue_collapse(res, ...)
}
}
glue("{1:5*}\n{letters[1:5]*}", .transformer = collapse_transformer(sep = ", "))
glue("{1:5*}\n{letters[1:5]*}", .transformer = collapse_transformer(sep = ", ", last = " and "))
## ------------------------------------------------------------------------
shell_transformer <- function(type = c("sh", "csh", "cmd", "cmd2")) {
type <- match.arg(type)
function(text, envir) {
res <- eval(parse(text = text, keep.source = FALSE), envir)
shQuote(res)
}
}
glue_sh <- function(..., .envir = parent.frame(), .type = c("sh", "csh", "cmd", "cmd2")) {
.type <- match.arg(.type)
glue(..., .envir = .envir, .transformer = shell_transformer(.type))
}
filename <- "test"
writeLines(con = filename, "hello!")
command <- glue_sh("cat {filename}")
command
system(command)
## ---- eval = require("emo")----------------------------------------------
emoji_transformer <- function(text, envir) {
if (grepl("[*]$", text)) {
text <- sub("[*]$", "", text)
glue_collapse(ji_find(text)$emoji)
} else {
ji(text)
}
}
glue_ji <- function(..., .envir = parent.frame()) {
glue(..., .open = ":", .close = ":", .envir = .envir, .transformer = emoji_transformer)
}
glue_ji("one :heart:")
glue_ji("many :heart*:")
## ------------------------------------------------------------------------
sprintf_transformer <- function(text, envir) {
m <- regexpr(":.+$", text)
if (m != -1) {
format <- substring(regmatches(text, m), 2)
regmatches(text, m) <- ""
res <- eval(parse(text = text, keep.source = FALSE), envir)
do.call(sprintf, list(glue("%{format}f"), res))
} else {
eval(parse(text = text, keep.source = FALSE), envir)
}
}
glue_fmt <- function(..., .envir = parent.frame()) {
glue(..., .transformer = sprintf_transformer, .envir = .envir)
}
glue_fmt("π = {pi:.2}")
## ------------------------------------------------------------------------
safely_transformer <- function(otherwise = NA) {
function(text, envir) {
tryCatch(
eval(parse(text = text, keep.source = FALSE), envir),
error = function(e) if (is.language(otherwise)) eval(otherwise) else otherwise)
}
}
glue_safely <- function(..., .otherwise = NA, .envir = parent.frame()) {
glue(..., .transformer = safely_transformer(.otherwise), .envir = .envir)
}
# Default returns missing if there is an error
glue_safely("foo: {xyz}")
# Or an empty string
glue_safely("foo: {xyz}", .otherwise = "Error")
# Or output the error message in red
library(crayon)
glue_safely("foo: {xyz}", .otherwise = quote(glue("{red}Error: {conditionMessage(e)}{reset}")))
glue/tests/ 0000755 0001762 0000144 00000000000 13263140232 012344 5 ustar ligges users glue/tests/testthat.R 0000644 0001762 0000144 00000000064 13263140232 014327 0 ustar ligges users library(testthat)
library(glue)
test_check("glue")
glue/tests/testthat/ 0000755 0001762 0000144 00000000000 13442031352 014205 5 ustar ligges users glue/tests/testthat/test-glue.R 0000644 0001762 0000144 00000024644 13441505634 016263 0 ustar ligges users context("glue")
test_that("inputs are concatenated, interpolated variables recycled", {
expect_equal(glue("test", "a", "string", "{1:2}"), c("testastring1", "testastring2"))
})
test_that("glue errors if the expression fails", {
expect_error(glue("{NoTfOuNd}"), "object .* not found")
})
test_that("glue errors if invalid format", {
expect_error(glue("x={x"), "Expecting '}'")
})
test_that("glue returns length 1 string from length 1 input", {
expect_equal(glue(""), "")
})
test_that("glue works with single expressions", {
foo <- "foo"
expect_equal(glue("{foo}"), foo)
foo <- 1L
expect_identical(glue("{foo}"), as_glue(foo))
foo <- as.raw(1)
expect_identical(glue("{foo}"), as_glue(foo))
foo <- TRUE
expect_identical(glue("{foo}"), as_glue(foo))
foo <- as.Date("2016-01-01")
expect_identical(glue("{foo}"), as_glue(foo))
})
test_that("glue works with repeated expressions", {
foo <- "foo"
expect_equal(glue("{foo} {foo}"), paste(foo, foo))
foo <- 1L
expect_equal(glue("{foo} {foo}"), paste(as.character(foo), as.character(foo)))
foo <- as.raw(1)
expect_equal(glue("{foo} {foo}"), paste(as.character(foo), as.character(foo)))
foo <- TRUE
expect_equal(glue("{foo} {foo}"), paste(as.character(foo), as.character(foo)))
foo <- as.Date("2016-01-01")
expect_equal(glue("{foo} {foo}"), paste(as.character(foo), as.character(foo)))
})
test_that("glue works with multiple expressions", {
foo <- "foo"
bar <- "bar"
expect_equal(glue("{foo} {bar}"), paste(foo, bar))
foo <- 1L
bar <- 2L
expect_equal(glue("{foo} {bar}"), paste(as.character(foo), as.character(bar)))
foo <- as.raw(1)
bar <- as.raw(2)
expect_equal(glue("{foo} {bar}"), paste(as.character(foo), as.character(bar)))
foo <- TRUE
bar <- FALSE
expect_equal(glue("{foo} {bar}"), paste(as.character(foo), as.character(bar)))
foo <- as.Date("2016-01-01")
bar <- as.Date("2016-01-02")
expect_equal(glue("{foo} {bar}"), paste(as.character(foo), as.character(bar)))
})
test_that("glue with doubled braces are converted glue single braces", {
expect_equal(glue("{{foo}}"), "{foo}")
})
test_that("glue works with complex expressions", {
`foo}\`` <- "foo"
expect_equal(glue("{
{
'}\\'' # { and } in comments, single quotes
\"}\\\"\" # or double quotes are ignored
`foo}\\`` # as are { in backticks
}
}"),
`foo}\``)
})
test_that("glue works with large outputs", {
# initial buffer allocates input string length + 1024, 40 * 26 = 1040
foo <- paste(rep(letters, 40), collapse = "")
# re-allocation on result
expect_equal(glue("{foo}"), foo)
# re-allocation on input
bar <- paste(rep(letters, 40), collapse = "")
additional <- " some more text that requires an allocation"
expect_equal(glue("{bar}", additional), paste0(bar, additional))
})
test_that("glue works with named arguments", {
name <- "Fred"
res <- glue('My name is {name},',
' my age next year is {age + 1},',
' a dot is a {.}',
name = "Joe",
age = 40,
. = "'.'")
expect_equal(
res,
"My name is Joe, my age next year is 41, a dot is a '.'"
)
expect_identical(name, "Fred")
})
test_that("glue evaluates arguments in the expected environment", {
x <- 2
fun <- function() {
x <- 1
glue("x: {x}, x+1: {y}", y = x + 1, .envir = parent.frame())
}
expect_equal(fun(), "x: 2, x+1: 3")
})
test_that("glue assigns arguments in the environment", {
expect_equal(glue("{b}", a = 1, b = a), "1")
})
test_that("error if non length 1 inputs", {
expect_error(glue(1:2, "{1:2}"), "All unnamed arguments must be length 1")
})
test_that("error if not simple recycling", {
expect_error(glue("{1:2}{1:10}"), "Variables must be length 1 or 10")
})
test_that("recycle_columns returns if zero length input", {
expect_identical(recycle_columns(list()), list())
expect_identical(recycle_columns(list(character())), character())
})
test_that("glue_data evaluates in the object first, then enclosure, then parent", {
x <- 1
y <- 1
z <- 1
fun <- function(env = environment()) {
y <- 2
glue_data(list(x = 3), "{x} {y} {z}", .envir = env)
}
# The function environment
expect_equal(fun(), "3 2 1")
# This environment
env <- environment()
expect_equal(fun(env), "3 1 1")
# A new environment
env2 <- new.env(parent = emptyenv())
env2$x <- 3
env2$y <- 3
env2$z <- 3
expect_equal(glue_data(env2, "{x} {y} {z}"), "3 3 3")
})
test_that("glue_data lazily evaluates named interpolation variables, in order", {
# Decoy 'x', which should not be evaluated
delayedAssign("x", stop("This 'x' shouldn't have been referenced"))
env <- new.env()
env$x <- "blah"
expect_equal(
glue_data(.x = env, "{x}{z}", y = stop("!"), z = x),
"blahblah"
)
expect_equal(
glue_data(.x = env, "{x}{z}", z = x, y = stop("!")),
"blahblah"
)
expect_equal(
glue_data(.x = list(x = "blah"), "{x}{z}", y = stop("!"), z = x),
"blahblah"
)
expect_equal(
glue_data(.x = list(x = "blah"), "{x}{z}", z = x, y = stop("!")),
"blahblah"
)
expect_equal(
glue_data(.x = NULL, "{x}{z}", x = "blah", y = stop("!"), z = x),
"blahblah"
)
expect_equal(
glue_data(.x = NULL, "blahblah", y = stop("!"), z = x),
"blahblah"
)
expect_equal(
glue_data(.x = NULL, "blahblah", x = x, y = stop("!"), z = x),
"blahblah"
)
})
test_that("converting glue to character", {
expect_identical(as.character(glue("foo bar")), "foo bar")
})
test_that("converting glue to glue", {
expect_equal(glue("foo bar"), "foo bar")
})
test_that("printing glue identical to cat()", {
expect_output(print(glue("foo\nbar")), "foo\nbar")
})
test_that("length 0 inputs produce length 0 outputs", {
expect_equal(glue("foo", character(0)), character(0))
expect_equal(glue("foo", NULL), character(0))
expect_equal(glue("foo", NULL, "bar"), character(0))
expect_equal(glue("foo", "{character(0)}"), character(0))
expect_equal(glue("foo {character(0)}"), character(0))
})
test_that("values are trimmed before evaluation", {
x <- " a1\n b2\n c3"
expect_equal(
glue("
A
{x}
B
"),
"A
a1
b2
c3
B")
})
test_that("glue works with alternative delimiters", {
expect_equal(glue("{1}", .open = "", .close = ""), "{1}")
expect_equal(glue("{{}}", .open = "", .close = ""), "{{}}")
expect_equal(glue("<<1>>", .open = "<<", .close = ">>"), "1")
expect_equal(glue("<<<<>>>>", .open = "<<", .close = ">>"), "<<>>")
expect_equal(glue("{{1}}", .open = "{{", .close = "}}"), "1")
expect_equal(glue("{{ {{1}} }}", .open = "{{", .close = "}}"), "1")
expect_equal(glue("{{ {{{1}}} }}", .open = "{{", .close = "}}"), "1")
expect_equal(glue("{{ {{{{1}}}} }}", .open = "{{", .close = "}}"), "1")
expect_equal(glue("[letters[[1]]]", .open = "[", .close = "]"), "a")
expect_equal(glue("[[ letters[[1]] ]]", .open = "[[", .close = "]]"), "a")
})
test_that("glue always returns UTF-8 encoded strings regardless of input encodings", {
x <- "fa\xE7ile"
Encoding(x) <- "latin1"
x_out <- as_glue(enc2utf8(x))
expect_identical(glue(x), x_out)
expect_identical(glue("{x}"), x_out)
expect_equal(Encoding(glue(x)), "UTF-8")
expect_equal(Encoding(glue("{x}")), "UTF-8")
y <- "p\u00E4o"
Encoding(y) <- "UTF-8"
y_out <- as_glue(enc2utf8(y))
expect_identical(glue(y), y_out)
expect_identical(glue("{y}"), y_out)
expect_equal(Encoding(glue(y)), "UTF-8")
expect_equal(Encoding(glue("{y}")), "UTF-8")
xy_out <- as_glue(paste0(x_out, y_out))
expect_identical(glue(x, y), xy_out)
expect_identical(glue("{x}{y}"), xy_out)
expect_equal(Encoding(glue(x, y)), "UTF-8")
expect_equal(Encoding(glue("{x}{y}")), "UTF-8")
expect_equal(Encoding(glue_collapse(x)), "UTF-8")
skip_on_os(c("mac", "linux", "solaris"))
withr::with_locale(c(LC_CTYPE = "Chinese (Simplified)_China.936"), {
z <- "{format(as.Date(\"2018-01-04\"), \"%Y\U5E74\")}"
z_out <- glue(z)
expect_equal(Encoding(z_out), "UTF-8")
expect_equal(z_out, "2018\U5E74")
})
})
test_that("glue always returns NA_character_ if given any NA input and `.na` == NULL", {
expect_equal(
glue("{NA}", .na = NULL),
NA_character_)
expect_equal(
glue(NA, .na = NULL),
NA_character_)
expect_equal(
glue(NA, 1, .na = NULL),
NA_character_)
expect_equal(
glue(1, NA, 2, .na = NULL),
NA_character_)
x <- c("foo", NA_character_, "bar")
expect_equal(
glue("{x}", .na = NULL),
c("foo", NA_character_, "bar"))
expect_equal(
glue("{1:3} - {x}", .na = NULL),
c("1 - foo", NA_character_, "3 - bar"))
})
test_that("glue always returns .na if given any NA input and `.na` != NULL", {
expect_equal(
glue("{NA}", .na = "foo"),
"foo")
expect_equal(
glue("{NA}", .na = "foo"),
"foo")
expect_equal(
glue(NA, .na = "foo"),
"foo")
expect_equal(
glue(NA, 1, .na = "foo"),
"foo1")
expect_equal(
glue(1, NA, 2, .na = "foo"),
"1foo2")
x <- c("foo", NA_character_, "bar")
expect_equal(
glue("{x}", .na = "baz"),
c("foo", "baz", "bar"))
expect_equal(
glue("{1:3} - {x}", .na = "baz"),
c("1 - foo", "2 - baz", "3 - bar"))
})
test_that("glue works within functions", {
x <- 1
f <- function(msg) glue(msg, .envir = parent.frame())
expect_equal(f("{x}"), "1")
})
test_that("scoping works within lapply (#42)", {
f <- function(msg) {
glue(msg, .envir = parent.frame())
}
expect_identical(lapply(1:2, function(x) f("{x * 2}")),
list(as_glue("2"), as_glue("4")))
})
test_that("glue works with lots of arguments", {
expect_equal(
glue("a", "very", "long", "test", "of", "how", "many", "unnamed",
"arguments", "you", "can", "have"),
"averylongtestofhowmanyunnamedargumentsyoucanhave")
})
test_that("glue does not drop it's class when subsetting", {
expect_equal(glue("foo")[1], "foo")
expect_equal(glue("foo")[[1]], "foo")
expect_equal(glue("{1:2}")[2], "2")
})
test_that("interpolation variables can have same names as their values (#89)", {
x <- 1
expect_equal(
glue("{x}", x = x + 1),
"2")
})
test_that("as_glue works", {
expect_identical(as_glue(as_glue("x")), as_glue("x"))
})
test_that("throws informative error if interpolating a function", {
expect_error(glue("{cat}"), "is a function")
# some crayon functions are OK, make sure this still works
if (require("crayon")) {
expect_is(glue("{red}red{reset}"), "glue")
}
})
test_that("+ method for glue works", {
expect_identical(glue("foo") + "bar", as_glue("foobar"))
x <- 1
expect_identical(glue("x = ") + "{x}", glue("x = {x}"))
})
glue/tests/testthat/test-collapse.R 0000644 0001762 0000144 00000003740 13413432727 017124 0 ustar ligges users context("glue_collapse")
test_that("glue_collapse works like paste(glue_collapse=)", {
# Always return 0 length outputs for 0 length inputs.
#expect_identical(paste(glue_collapse = "", character(0)), glue_collapse(character(0)))
expect_identical(as_glue(paste(collapse = "", "")), glue_collapse(""))
expect_identical(as_glue(paste(collapse = "", 1:10)), glue_collapse(1:10))
expect_identical(as_glue(paste(collapse = " ", 1:10)), glue_collapse(1:10, sep = " "))
})
test_that("glue_collapse truncates", {
expect_identical(as_glue("12345678910"), glue_collapse(1:10, width = 11))
expect_identical(as_glue("12345678910"), glue_collapse(1:10, width = 100))
expect_identical(as_glue("1234567..."), glue_collapse(1:10, width = 10))
expect_identical(as_glue("123..."), glue_collapse(1:10, width = 6))
expect_identical(as_glue("1..."), glue_collapse(1:10, width = 4))
expect_identical(as_glue("..."), glue_collapse(1:10, width = 0))
})
test_that("last argument to glue_collapse", {
expect_equal(glue_collapse(character(), last = " and "), as_glue(character()))
expect_equal(glue_collapse("", last = " and "), as_glue(""))
expect_equal(glue_collapse(1, last = " and "), as_glue("1"))
expect_equal(glue_collapse(1:2, last = " and "),as_glue( "1 and 2"))
expect_equal(glue_collapse(1:4, ", ", last = " and "), as_glue("1, 2, 3 and 4"))
expect_equal(glue_collapse(1:4, ", ", last = " and ", width = 5), as_glue("1,..."))
expect_equal(glue_collapse(1:4, ", ", last = " and ", width = 10), as_glue("1, 2, 3..."))
})
test_that("glue_collapse returns 0 length output for 0 length input", {
expect_identical(glue_collapse(character()), as_glue(character()))
})
test_that("glue_collapse returns NA_character_ if any inputs are NA", {
expect_identical(glue_collapse(NA_character_), as_glue(NA_character_))
expect_identical(glue_collapse(c(1, 2, 3, NA_character_)), as_glue(NA_character_))
expect_identical(glue_collapse(c("foo", NA_character_, "bar")), as_glue(NA_character_))
})
glue/tests/testthat/test-quoting.R 0000644 0001762 0000144 00000001364 13255211375 017006 0 ustar ligges users context("quoting")
test_that("single_quote works", {
expect_identical(single_quote(character()), character())
expect_identical(single_quote(""), "''")
expect_identical(single_quote(1:5),
c("'1'",
"'2'",
"'3'",
"'4'",
"'5'"
))
})
test_that("double_quote works", {
expect_identical(double_quote(character()), character())
expect_identical(double_quote(""), '""')
expect_identical(double_quote(1:5),
c('"1"',
'"2"',
'"3"',
'"4"',
'"5"'
))
})
test_that("backtick works", {
expect_identical(backtick(character()), character())
expect_identical(backtick(""), '``')
expect_identical(backtick(1:5),
c("`1`",
"`2`",
"`3`",
"`4`",
"`5`"
))
})
glue/tests/testthat/test-color.R 0000644 0001762 0000144 00000003172 13413432727 016437 0 ustar ligges users context("color")
skip_if_not_installed("crayon")
library(crayon)
describe("glue_col", {
it("returns the string if no substations needed", {
expect_identical(glue_col("foo"), as_glue("foo"))
})
it("works the same as glue for parsable expressions", {
expect_identical(glue_col("1 + 1 = {1 + 1}"), glue("1 + 1 = {1 + 1}"))
})
it("applies crayon functions", {
expect_identical(glue_col("{blue foo}"), as_glue(blue("foo")))
blue_and_white <- bgBlue $ white
expect_identical(glue_col("{blue_and_white foo}"), as_glue(blue_and_white("foo")))
expect_identical(glue_col("{blue_and_white {1 + 1}}"), as_glue(blue_and_white("2")))
})
it("works on multiline strings", {
expect_identical(
glue_col("
{red foo
bar
}"), as_glue(red("foo\nbar")))
})
it("works on nested colors", {
expect_identical(glue_col("{red This is a {green serious} problem}"),
as_glue(red("This is a " %+% green("serious") %+% " problem")))
})
it("errors if there is invalid syntax or fun is not found", {
expect_error(glue_col("{_}"), "unexpected input")
expect_error(glue_col("{foo _}"), "object 'foo' of mode 'function' was not found")
foo <- 1
expect_error(glue_col("{foo _}"), "object 'foo' of mode 'function' was not found")
foo <- crayon::blue
expect_identical(glue_col("{foo _}"), as_glue(foo("_")))
})
})
describe("glue_data_col", {
it("works as expected", {
mt <- head(mtcars)
expect_identical(glue_data_col(mt, "A {blue {gear}} speed car with {bold {hp}} hp!"),
as_glue("A " %+% blue(mt$gear) %+% " speed car with " %+% bold(mt$hp) %+% " hp!"))
})
})
glue/tests/testthat/test-trim.R 0000644 0001762 0000144 00000004251 13255211375 016271 0 ustar ligges users context("trim")
test_that("trim works", {
expect_identical("", trim(""))
expect_identical(character(), trim(character()))
expect_identical(" ", trim(" "))
expect_identical("test", trim("test"))
expect_identical(" test", trim(" test"))
expect_identical("test ", trim("test "))
expect_identical("test", trim("test"))
expect_identical(c("foo", "bar"), trim(c("foo", "bar")))
expect_identical(c("foo", "bar"), trim(c("\nfoo", "bar\n")))
expect_identical("test",
trim(
"test"))
expect_identical("test",
x <- trim(
"test
"))
expect_identical("test",
trim("
test
"))
expect_identical("test",
trim(
"test"))
expect_identical("test\n test2",
trim("
test
test2
"))
expect_identical("test\n test2\n test3",
trim("
test
test2
test3
"))
expect_identical("\ntest\n",
trim("
test
"))
})
test_that("trim strips escaped newlines", {
expect_identical(
"foo bar baz",
trim("foo bar \\\nbaz"))
expect_identical(
trim("
foo bar \\
baz"),
"foo bar baz")
expect_identical(
trim("
foo bar \\
baz
"),
"foo bar baz")
expect_identical(
"foo bar baz\n",
trim("foo bar baz\n\n"))
expect_identical(
"\nfoo bar baz",
trim("\n\nfoo bar baz"))
})
test_that("issue#44", {
expect_identical(
trim("12345678
foo
bar
baz
bar
baz"),
"12345678\n foo\n bar\nbaz\n bar\n baz")
})
test_that("issue#47", {
expect_identical(
trim("
Hello,
World.
"),
" Hello,\n World.")
expect_identical(
trim("
foo
bar
123456789"),
"foo\n bar\n 123456789")
expected <- "The stuff before the bullet list\n * one bullet"
expect_identical(
trim("The stuff before the bullet list
* one bullet
"), expected)
expect_identical(
trim("
The stuff before the bullet list
* one bullet"), expected)
expect_identical(
trim("
The stuff before the bullet list
* one bullet
"), expected)
})
glue/tests/testthat/test-sql.R 0000644 0001762 0000144 00000005527 13441473136 016126 0 ustar ligges users context("sql")
skip_if_not_installed("DBI")
skip_if_not_installed("RSQLite")
describe("glue_sql", {
con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
on.exit(DBI::dbDisconnect(con))
it("errors if no connection given", {
var <- "foo"
expect_error(glue_sql("{var}"), "missing")
})
it("returns the string if no substations needed", {
expect_identical(glue_sql("foo", .con = con), DBI::SQL("foo"))
})
it("quotes string values", {
var <- "foo"
expect_identical(glue_sql("{var}", .con = con), DBI::SQL("'foo'"))
})
it("quotes identifiers", {
var <- "foo"
expect_identical(glue_sql("{`var`}", .con = con), DBI::SQL("`foo`"))
})
it("quotes Id identifiers", {
var <- DBI::Id(schema = "foo", table = "bar", column = "baz")
expect_identical(glue_sql("{`var`}", .con = con), DBI::SQL("`foo`.`bar`.`baz`"))
})
it("quotes lists of Id identifiers", {
var <- c(
DBI::Id(schema = "foo", table = "bar", column = "baz"),
DBI::Id(schema = "foo", table = "bar", column = "baz2")
)
expect_identical(glue_sql("{`var`*}", .con = con), DBI::SQL("`foo`.`bar`.`baz`, `foo`.`bar`.`baz2`"))
})
it("Does not quote numbers", {
var <- 1
expect_identical(glue_sql("{var}", .con = con), DBI::SQL("1"))
})
it("Does not quote DBI::SQL()", {
var <- DBI::SQL("foo")
expect_identical(glue_sql("{var}", .con = con), DBI::SQL("foo"))
})
it("collapses values if succeeded by a *", {
expect_identical(glue_sql("{var*}", .con = con, var = 1), DBI::SQL(1))
expect_identical(glue_sql("{var*}", .con = con, var = 1:5), DBI::SQL("1, 2, 3, 4, 5"))
expect_identical(glue_sql("{var*}", .con = con, var = "a"), DBI::SQL("'a'"))
expect_identical(glue_sql("{var*}", .con = con, var = letters[1:5]), DBI::SQL("'a', 'b', 'c', 'd', 'e'"))
})
it("should return an SQL NULL by default for missing values", {
var <- list(NA, NA_character_, NA_real_, NA_integer_)
expect_identical(glue_sql("x = {var}", .con = con), rep(DBI::SQL("x = NULL"), 4))
})
it("should return NA for missing values and .na = NULL", {
var <- list(NA, NA_character_, NA_real_, NA_integer_)
expect_identical(glue_sql("x = {var}", .con = con, .na = NULL), rep(DBI::SQL(NA), 4))
})
it("should return NA for missing values quote strings", {
var <- c("C", NA)
expect_identical(glue_sql("x = {var}", .con = con), DBI::SQL(c("x = 'C'", "x = NULL")))
})
it("should return a quoted date for Dates", {
var <- as.Date("2019-01-01")
expect_identical(glue_sql("x = {var}", .con = con), DBI::SQL("x = '2019-01-01'"))
})
})
describe("glue_data_sql", {
con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
on.exit(DBI::dbDisconnect(con))
it("collapses values if succeeded by a *", {
var <- "foo"
expect_identical(glue_data_sql(mtcars, "{head(gear)*}", .con = con), DBI::SQL("4, 4, 4, 3, 3, 3"))
})
})
glue/src/ 0000755 0001762 0000144 00000000000 13441546417 012006 5 ustar ligges users glue/src/trim.c 0000644 0001762 0000144 00000004706 13441546417 013134 0 ustar ligges users #include "Rinternals.h"
#include
#include
#include // for strlen()
SEXP trim_(SEXP x) {
size_t len = LENGTH(x);
SEXP out = PROTECT(Rf_allocVector(STRSXP, len));
size_t num;
for (num = 0; num < len; ++num) {
const char* xx = Rf_translateCharUTF8(STRING_ELT(x, num));
size_t str_len = strlen(xx);
char* str = (char*)malloc(str_len + 1);
size_t i = 0, start = 0;
bool new_line = false;
/* skip leading blanks on first line */
while (start < str_len && (xx[start] == ' ' || xx[start] == '\t')) {
++start;
}
/* Skip first newline */
if (start < str_len && xx[start] == '\n') {
new_line = true;
++start;
}
i = start;
/* Ignore first line */
if (!new_line) {
while (i < str_len && xx[i] != '\n') {
++i;
}
new_line = true;
}
size_t indent = 0;
/* Maximum size of size_t */
size_t min_indent = (size_t)-1;
/* find minimum indent */
while (i < str_len) {
if (xx[i] == '\n') {
new_line = true;
} else if (new_line) {
if (xx[i] == ' ' || xx[i] == '\t') {
++indent;
} else {
if (indent < min_indent) {
min_indent = indent;
}
indent = 0;
new_line = false;
}
}
++i;
}
if (new_line && indent < min_indent) {
min_indent = indent;
}
new_line = true;
i = start;
size_t j = 0;
/*Rprintf("start: %i\nindent: %i\nmin_indent: %i", start, indent,
* min_indent);*/
/* copy the string removing the minimum indent from new lines */
while (i < str_len) {
if (xx[i] == '\n') {
new_line = true;
} else if (xx[i] == '\\' && i + 1 < str_len && xx[i + 1] == '\n') {
new_line = true;
i += 2;
continue;
} else if (new_line) {
if (i + min_indent < str_len && (xx[i] == ' ' || xx[i] == '\t')) {
i += min_indent;
}
new_line = false;
}
str[j++] = xx[i++];
}
str[j] = '\0';
/* Remove trailing whitespace up to the first newline */
size_t end = j;
while (j > 0) {
if (str[j] == '\n') {
end = j;
break;
} else if (str[j] == '\0' || str[j] == ' ' || str[j] == '\t') {
--j;
} else {
break;
}
}
str[end] = '\0';
SET_STRING_ELT(out, num, Rf_mkCharCE(str, CE_UTF8));
free(str);
}
UNPROTECT(1);
return out;
}
glue/src/init.c 0000644 0001762 0000144 00000001013 13441546417 013110 0 ustar ligges users #include
#include
#include
#include // for NULL
/* .Call calls */
extern SEXP glue_(SEXP, SEXP);
extern SEXP trim_(SEXP);
static const R_CallMethodDef CallEntries[] = {{"glue_", (DL_FUNC)&glue_, 4},
{"trim_", (DL_FUNC)&trim_, 1},
{NULL, NULL, 0}};
void R_init_glue(DllInfo *dll) {
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
R_useDynamicSymbols(dll, FALSE);
}
glue/src/glue.c 0000644 0001762 0000144 00000010130 13441546417 013101 0 ustar ligges users #include "Rinternals.h"
#include
#include
SEXP set(SEXP x, int i, SEXP val) {
R_xlen_t len = Rf_xlength(x);
if (i >= len) {
len *= 2;
x = Rf_lengthgets(x, len);
}
SET_VECTOR_ELT(x, i, val);
return x;
}
SEXP resize(SEXP out, R_xlen_t n) {
if (n == Rf_xlength(out)) {
return out;
}
return Rf_xlengthgets(out, n);
}
SEXP glue_(SEXP x, SEXP f, SEXP open_arg, SEXP close_arg) {
typedef enum {
text,
escape,
single_quote,
double_quote,
backtick,
delim,
comment
} states;
const char* xx = Rf_translateCharUTF8(STRING_ELT(x, 0));
size_t str_len = strlen(xx);
char* str = (char*)malloc(str_len + 1);
const char* open = CHAR(STRING_ELT(open_arg, 0));
size_t open_len = strlen(open);
const char* close = CHAR(STRING_ELT(close_arg, 0));
size_t close_len = strlen(close);
int delim_equal = strncmp(open, close, open_len) == 0;
SEXP out = Rf_allocVector(VECSXP, 1);
PROTECT_INDEX out_idx;
PROTECT_WITH_INDEX(out, &out_idx);
size_t j = 0;
size_t k = 0;
int delim_level = 0;
size_t start = 0;
states state = text;
states prev_state = text;
size_t i = 0;
for (i = 0; i < str_len; ++i) {
switch (state) {
case text: {
if (strncmp(&xx[i], open, open_len) == 0) {
/* check for open delim doubled */
if (strncmp(&xx[i + open_len], open, open_len) == 0) {
i += open_len;
} else {
state = delim;
delim_level = 1;
start = i + open_len;
break;
}
}
if (strncmp(&xx[i], close, close_len) == 0 &&
strncmp(&xx[i + close_len], close, close_len) == 0) {
i += close_len;
}
str[j++] = xx[i];
break;
}
case escape: {
state = prev_state;
break;
}
case single_quote: {
if (xx[i] == '\\') {
prev_state = single_quote;
state = escape;
} else if (xx[i] == '\'') {
state = delim;
}
break;
}
case double_quote: {
if (xx[i] == '\\') {
prev_state = double_quote;
state = escape;
} else if (xx[i] == '\"') {
state = delim;
}
break;
}
case backtick: {
if (xx[i] == '\\') {
prev_state = backtick;
state = escape;
} else if (xx[i] == '`') {
state = delim;
}
break;
}
case comment: {
if (xx[i] == '\n') {
state = delim;
}
break;
}
case delim: {
if (!delim_equal && strncmp(&xx[i], open, open_len) == 0) {
++delim_level;
i += open_len - 1;
} else if (strncmp(&xx[i], close, close_len) == 0) {
--delim_level;
i += close_len - 1;
} else {
switch (xx[i]) {
case '\'':
state = single_quote;
break;
case '"':
state = double_quote;
break;
case '`':
state = backtick;
break;
case '#':
state = comment;
break;
};
}
if (delim_level == 0) {
/* Result of the current glue statement */
SEXP expr = PROTECT(Rf_ScalarString(
Rf_mkCharLenCE(&xx[start], (i - close_len) + 1 - start, CE_UTF8)));
SEXP call = PROTECT(Rf_lang2(f, expr));
SEXP result = PROTECT(Rf_eval(call, R_GlobalEnv));
/* text in between last glue statement */
if (j > 0) {
str[j] = '\0';
SEXP str_ = PROTECT(Rf_ScalarString(Rf_mkCharLenCE(str, j, CE_UTF8)));
REPROTECT(out = set(out, k++, str_), out_idx);
UNPROTECT(1);
}
REPROTECT(out = set(out, k++, result), out_idx);
/* Clear the string buffer */
memset(str, 0, j);
j = 0;
UNPROTECT(3);
state = text;
}
break;
}
};
}
if (k == 0 || j > 0) {
str[j] = '\0';
SEXP str_ = PROTECT(Rf_ScalarString(Rf_mkCharLenCE(str, j, CE_UTF8)));
REPROTECT(out = set(out, k++, str_), out_idx);
UNPROTECT(1);
}
if (state == delim) {
Rf_error("Expecting '%s'", close);
}
free(str);
out = resize(out, k);
UNPROTECT(1);
return out;
}
glue/NAMESPACE 0000644 0001762 0000144 00000001073 13441501306 012423 0 ustar ligges users # Generated by roxygen2: do not edit by hand
S3method("+",glue)
S3method("[",glue)
S3method("[[",glue)
S3method(as.character,glue)
S3method(as_glue,character)
S3method(as_glue,default)
S3method(as_glue,glue)
S3method(print,glue)
export(as_glue)
export(backtick)
export(collapse)
export(double_quote)
export(glue)
export(glue_col)
export(glue_collapse)
export(glue_data)
export(glue_data_col)
export(glue_data_sql)
export(glue_sql)
export(identity_transformer)
export(single_quote)
export(trim)
importFrom(methods,setOldClass)
useDynLib(glue,glue_)
useDynLib(glue,trim_)
glue/.aspell/ 0000755 0001762 0000144 00000000000 13260504460 012544 5 ustar ligges users glue/.aspell/defaults.R 0000644 0001762 0000144 00000000231 13260504460 014472 0 ustar ligges users Rd_files <- vignettes <- R_files <- description <-
list(encoding = "UTF-8",
language = "en",
dictionaries = c("en_stats", "glue"))
glue/.aspell/glue.rds 0000644 0001762 0000144 00000000070 13260504460 014207 0 ustar ligges users b```b`fab`b2Hs'e |]c( glue/NEWS.md 0000644 0001762 0000144 00000005707 13441541035 012315 0 ustar ligges users # glue 1.3.1
## Features
* `glue()` now has a `+` method to combine strings.
## Bugfixes and minor changes
* `glue_sql()` now supports unquoting lists of Id objects.
* `glue_sql()` now quotes characters with NAs appropriately (#115).
* `glue_sql()` now quotes Dates appropriately (#98).
* A potential protection error reported by rchk was fixed.
# glue 1.3.0
## Breaking changes
* The `evaluate()` function has been removed. Changes elsewhere in glue made
the implementation trivial so it was removed for clarities sake. Previous
uses can be replaced by `eval(parse(text = text), envir)`.
* `collapse()` has been renamed to `glue_collapse()` to avoid namespace
collisions with `dplyr::collapse()`.
## Features
* `compare.glue()` was added, to make it easier to use glue objects in
`testthat::expect_equal()` statements.
* `glue_col()` and `glue_data_col()` functions added to display strings with
color.
## Bugfixes and minor changes
* Glue now throws an informative error message when it cannot interpolate a
function into a string (#114, @haleyjeppson & @ijlyttle).
* Glue now evaluates unnamed arguments lazily with `delayedAssign()`, so there
is no performance cost if an argument is not used. (#83, @egnha).
* Fixed a bug where names in the assigned expression of an interpolation
variable would conflict with the name of the variable itself (#89, @egnha).
* Do not drop the `glue` class when subsetting (#66).
* Fix `glue()` and `collapse()` always return UTF-8 encoded strings (#81, @dpprdan)
# glue 1.2.0
* The implementation has been tweaked to be slightly faster in most cases.
* `glue()` now has a `.transformer` argument, which allows you to use custom
logic on how to evaluate the code within glue blocks. See
`vignette("transformers")` for more details and example transformer
functions.
* `glue()` now returns `NA` if any of the results are `NA` and `.na` is `NULL`.
Otherwise `NA` values are replaced by the value of `.na`.
* `trim()` to use the trimming logic from glue is now exported.
* `glue_sql()` and `glue_data_sql()` functions added to make constructing SQL
statements with glue safer and easier.
* `glue()` is now easier to use when used within helper functions such as
`lapply`.
* Fix when last expression in `glue()` is NULL.
# glue 1.1.1
* Another fix for PROTECT / REPROTECT found by the rchk static analyzer.
# glue 1.1.0
* Fix for PROTECT errors when resizing output strings.
* `glue()` always returns 'UTF-8' strings, converting inputs if in other
encodings if needed.
* `to()` and `to_data()` have been removed.
* `glue()` and `glue_data()` can now take alternative delimiters to `{` and `}`.
This is useful if you are writing to a format that uses a lot of braces, such
as LaTeX. (#23)
* `collapse()` now returns 0 length output if given 0 length input (#28).
# glue 0.0.0.9000
* Fix `glue()` to admit `.` as an embedded expression in a string (#15, @egnha).
* Added a `NEWS.md` file to track changes to the package.
glue/R/ 0000755 0001762 0000144 00000000000 13441506103 011404 5 ustar ligges users glue/R/utils.R 0000644 0001762 0000144 00000003566 13413432727 012712 0 ustar ligges users has_names <- function(x) {
nms <- names(x)
if (is.null(nms)) {
rep(FALSE, length(x))
} else {
!(is.na(nms) | nms == "")
}
}
bind_args <- function(args, parent) {
assign_env <- parent
nms <- names(args)
for (i in seq_along(args)) {
eval_env <- assign_env
assign_env <- new.env(parent = eval_env)
delayed_assign(nms[[i]], args[[i]], eval.env = eval_env, assign.env = assign_env)
}
assign_env
}
# From tibble::recycle_columns
recycle_columns <- function (x) {
if (length(x) == 0) {
return(x)
}
lengths <- vapply(x, NROW, integer(1))
if (any(lengths) == 0) {
return(character())
}
max <- max(lengths)
bad_len <- lengths != 1L & lengths != max
if (any(bad_len)) {
stop(call. = FALSE,
ngettext(max,
"Variables must be length 1",
paste0("Variables must be length 1 or ", max), domain = NA))
}
short <- lengths == 1
if (max != 1L && any(short)) {
x[short] <- lapply(x[short], rep, max)
}
x
}
# From https://github.com/hadley/colformat/blob/0a35999e7d77b9b3a47b4a04662d1c2625f929d3/R/styles.R#L19-L25
colour_na <- function() {
grDevices::rgb(5, 5, 2, maxColorValue = 5)
}
style_na <- function(x) {
if (requireNamespace("crayon", quietly = TRUE)) {
crayon::style(x, bg = colour_na())
} else {
x # nocov
}
}
lengths <- function(x) {
vapply(x, length, integer(1L))
}
na_rows <- function(res) {
Reduce(`|`, lapply(res, is.na))
}
"%||%" <- function(x, y) if (is.null(x)) y else x # nocov
# A version of delayedAssign which does _not_ use substitute
delayed_assign <- function(x, value, eval.env = parent.frame(1), assign.env = parent.frame(1)) {
do.call(delayedAssign, list(x, value, eval.env, assign.env))
}
## @export
compare.glue <- function(x, y) {
if (identical(class(y), "character")) {
class(x) <- NULL
}
NextMethod("compare")
}
glue/R/quoting.R 0000644 0001762 0000144 00000001061 13413432727 013224 0 ustar ligges users #' Quoting operators
#'
#' These functions make it easy to quote each individual element and are useful
#' in conjunction with `glue_collapse()`.
#' @param x A character to quote.
#' @name quoting
#' @export
#' @examples
#' x <- 1:5
#' glue('Values of x: {glue_collapse(backtick(x), sep = ", ", last = " and ")}')
single_quote <- function(x) {
encodeString(x, quote = "'")
}
#' @rdname quoting
#' @export
double_quote <- function(x) {
encodeString(x, quote = '"')
}
#' @rdname quoting
#' @export
backtick <- function(x) {
encodeString(x, quote = "`")
}
glue/R/knitr.R 0000644 0001762 0000144 00000002250 13260504460 012660 0 ustar ligges users # nocov start
eng_glue <- function(options) {
glue_options <- options[names(options) %in% names(formals(glue))]
glue_options$.envir <- glue_options$.envir %||% knitr::knit_global()
out <- do.call(glue, c(list(options$code), glue_options))
knitr::engine_output(options, options$code, out)
}
eng_glue_sql <- function(options) {
glue_sql_options <- options[names(options) %in% names(formals(glue_sql))]
glue_sql_options$.con <- glue_sql_options$.con %||% options$connection
glue_sql_options$.envir <- glue_sql_options$.envir %||% knitr::knit_global()
con <- glue_sql_options$.con
if (is.character(con)) {
con <- get(con, envir = knitr::knit_global())
}
if (is.null(con)) {
stop(.call = FALSE,
"The 'connection' option (DBI connection) is required for glue_sql chunks.")
}
glue_sql_options$.con <- con
options$code <- do.call(glue_sql, c(list(options$code), glue_sql_options))
options$engine <- "sql"
knitr::knit_engines$get("sql")(options)
}
.onLoad <- function(libname, pkgname) {
if (requireNamespace("knitr", quietly = TRUE)) {
knitr::knit_engines$set(glue = eng_glue, glue_sql = eng_glue_sql, gluesql = eng_glue_sql)
}
}
# nocov end
glue/R/transformer.R 0000644 0001762 0000144 00000000744 13413432727 014107 0 ustar ligges users #' Parse and Evaluate R code
#'
#' This is a simple wrapper around `eval(parse())`, used as the default
#' transformer.
#' @param text Text (typically) R code to parse and evaluate.
#' @param envir environment to evaluate the code in
#' @seealso `vignette("transformers", "glue")` for documentation on creating
#' custom glue transformers and some common use cases.
#' @export
identity_transformer <- function(text, envir) {
eval(parse(text = text, keep.source = FALSE), envir)
}
glue/R/glue.R 0000644 0001762 0000144 00000021473 13441506103 012472 0 ustar ligges users #' Format and interpolate a string
#'
#' Expressions enclosed by braces will be evaluated as R code. Long strings are
#' broken by line and concatenated together. Leading whitespace and blank lines
#' from the first and last lines are automatically trimmed.
#'
#' @param .x \[`listish`]\cr An environment, list or data frame used to lookup values.
#' @param ... \[`expressions`]\cr Expressions string(s) to format, multiple inputs are concatenated together before formatting.
#' @param .sep \[`character(1)`: \sQuote{""}]\cr Separator used to separate elements.
#' @param .envir \[`environment`: `parent.frame()`]\cr Environment to evaluate each expression in. Expressions are
#' evaluated from left to right. If `.x` is an environment, the expressions are
#' evaluated in that environment and `.envir` is ignored.
#' @param .open \[`character(1)`: \sQuote{\\\{}]\cr The opening delimiter. Doubling the
#' full delimiter escapes it.
#' @param .close \[`character(1)`: \sQuote{\\\}}]\cr The closing delimiter. Doubling the
#' full delimiter escapes it.
#' @param .transformer \[`function]`\cr A function taking three parameters `code`, `envir` and
#' `data` used to transform the output of each block before during or after
#' evaluation. For example transformers see `vignette("transformers")`.
#' @param .na \[`character(1)`: \sQuote{NA}]\cr Value to replace NA values
#' with. If `NULL` missing values are propagated, that is an `NA` result will
#' cause `NA` output. Otherwise the value is replaced by the value of `.na`.
#' @param .trim \[`logical(1)`: \sQuote{TRUE}]\cr Whether to trim the input
#' template with `trim()` or not.
#' @seealso and
#' upon which this is based.
#' @examples
#' name <- "Fred"
#' age <- 50
#' anniversary <- as.Date("1991-10-12")
#' glue('My name is {name},',
#' 'my age next year is {age + 1},',
#' 'my anniversary is {format(anniversary, "%A, %B %d, %Y")}.')
#'
#' # single braces can be inserted by doubling them
#' glue("My name is {name}, not {{name}}.")
#'
#' # Named arguments can be used to assign temporary variables.
#' glue('My name is {name},',
#' ' my age next year is {age + 1},',
#' ' my anniversary is {format(anniversary, "%A, %B %d, %Y")}.',
#' name = "Joe",
#' age = 40,
#' anniversary = as.Date("2001-10-12"))
#'
#'
#' # `glue_data()` is useful in magrittr pipes
#' library(magrittr)
#' mtcars %>% glue_data("{rownames(.)} has {hp} hp")
#'
#' # Or within dplyr pipelines
#' library(dplyr)
#' head(iris) %>%
#' mutate(description = glue("This {Species} has a petal length of {Petal.Length}"))
#'
#' # Alternative delimiters can also be used if needed
#' one <- "1"
#' glue("The value of $e^{2\\pi i}$ is $<>$.", .open = "<<", .close = ">>")
#' @useDynLib glue glue_
#' @name glue
#' @export
glue_data <- function(.x, ..., .sep = "", .envir = parent.frame(),
.open = "{", .close = "}", .na = "NA", .transformer = identity_transformer,
.trim = TRUE) {
# Perform all evaluations in a temporary environment
if (is.null(.x)) {
parent_env <- .envir
} else if (is.environment(.x)) {
parent_env <- .x
} else {
parent_env <- list2env(.x, parent = .envir)
}
# Capture unevaluated arguments
dots <- eval(substitute(alist(...)))
named <- has_names(dots)
# Evaluate named arguments, add results to environment
env <- bind_args(dots[named], parent_env)
# Concatenate unnamed arguments together
unnamed_args <- lapply(which(!named), function(x) eval(call("force", as.symbol(paste0("..", x)))))
lengths <- lengths(unnamed_args)
if (any(lengths == 0) || length(unnamed_args) < sum(!named)) {
return(as_glue(character(0)))
}
if (any(lengths != 1)) {
stop("All unnamed arguments must be length 1", call. = FALSE)
}
if (any(is.na(unnamed_args))) {
if (is.null(.na)) {
return(as_glue(NA_character_))
} else {
unnamed_args[is.na(unnamed_args)] <- .na
}
}
unnamed_args <- paste0(unnamed_args, collapse = .sep)
if (isTRUE(.trim)) {
unnamed_args <- trim(unnamed_args)
}
f <- function(expr){
eval_func <- .transformer(expr, env)
# crayon functions *can* be used, so we use tryCatch()
# to give as.character() a chance to work
tryCatch(
as.character(eval_func),
error = function(e) {
# if eval_func is a function, provide informative error-message
if (is.function(eval_func)) {
message <- paste0(
"glue cannot interpolate functions into strings.\n",
"* object '",
expr,
"' is a function."
)
stop(message, call. = FALSE)
}
# default stop
stop(e)
}
)
}
# Parse any glue strings
res <- .Call(glue_, unnamed_args, f, .open, .close)
if (any(lengths(res) == 0)) {
return(as_glue(character(0)))
}
if (!is.null(.na)) {
res[] <- lapply(res, function(x) replace(x, is.na(x), .na))
} else {
na_rows <- na_rows(res)
}
res <- do.call(paste0, recycle_columns(res))
if (is.null(.na)) {
res <- replace(res, na_rows, NA)
}
as_glue(res)
}
#' @export
#' @rdname glue
glue <- function(..., .sep = "", .envir = parent.frame(), .open = "{", .close = "}", .na = "NA", .transformer = identity_transformer) {
glue_data(.x = NULL, ..., .sep = .sep, .envir = .envir, .open = .open, .close = .close, .na = .na, .transformer = .transformer)
}
#' Collapse a character vector
#'
#' Collapses a character vector of any length into a length 1 vector.
#' @param x The character vector to collapse.
#' @param width The maximum string width before truncating with `...`.
#' @param last String used to separate the last two items if `x` has at least
#' 2 items.
#' @inheritParams base::paste
#' @examples
#' glue_collapse(glue("{1:10}"))
#'
#' # Wide values can be truncated
#' glue_collapse(glue("{1:10}"), width = 5)
#'
#' glue_collapse(1:4, ", ", last = " and ")
#' #> 1, 2, 3 and 4
#' @export
glue_collapse <- function(x, sep = "", width = Inf, last = "") {
if (length(x) == 0) {
return(as_glue(character()))
}
if (any(is.na(x))) {
return(as_glue(NA_character_))
}
if (nzchar(last) && length(x) > 1) {
res <- glue_collapse(x[seq(1, length(x) - 1)], sep = sep, width = Inf)
return(glue_collapse(paste0(res, last, x[length(x)]), width = width))
}
x <- paste0(x, collapse = sep)
if (width < Inf) {
x_width <- nchar(x, "width")
too_wide <- x_width > width
if (too_wide) {
x <- paste0(substr(x, 1, width - 3), "...")
}
}
as_glue(x)
}
# nocov start
#' @rdname glue-deprecated
#' @export
collapse <- function(x, sep = "", width = Inf, last = "") {
.Deprecated("glue_collapse", package = "glue")
glue_collapse(x, sep, width, last)
}
# nocov end
#' Trim a character vector
#'
#' This trims a character vector according to the trimming rules used by glue.
#' These follow similar rules to [Python Docstrings](https://www.python.org/dev/peps/pep-0257),
#' with the following features.
#' - Leading and trailing whitespace from the first and last lines is removed.
#' - A uniform amount of indentation is stripped from the second line on, equal
#' to the minimum indentation of all non-blank lines after the first.
#' - Lines can be continued across newlines by using `\\`.
#' @param x A character vector to trim.
#' @export
#' @examples
#' glue("
#' A formatted string
#' Can have multiple lines
#' with additional indention preserved
#' ")
#'
#' glue("
#' \\ntrailing or leading newlines can be added explicitly\\n
#' ")
#'
#' glue("
#' A formatted string \\
#' can also be on a \\
#' single line
#' ")
#' @useDynLib glue trim_
trim <- function(x) {
has_newline <- function(x) any(grepl("\\n", x))
if (length(x) == 0 || !has_newline(x)) {
return(x)
}
.Call(trim_, x)
}
#' @export
print.glue <- function(x, ..., sep = "\n") {
x[is.na(x)] <- style_na(x[is.na(x)])
cat(x, ..., sep = sep)
invisible(x)
}
#' Coerce object to glue
#' @param x object to be coerced.
#' @param ... further arguments passed to methods.
#' @export
as_glue <- function(x, ...) {
UseMethod("as_glue")
}
#' @export
as_glue.default <- function(x, ...) {
as_glue(as.character(x))
}
#' @export
as_glue.glue <- function(x, ...) {
x
}
#' @export
as_glue.character <- function(x, ...) {
class(x) <- c("glue", "character")
enc2utf8(x)
}
#' @export
as.character.glue <- function(x, ...) {
unclass(x)
}
#' @export
`[.glue` <- function(x, i, ...) {
as_glue(NextMethod())
}
#' @export
`[[.glue` <- function(x, i, ...) {
as_glue(NextMethod())
}
#' @export
`+.glue` <- function(e1, e2) {
glue(e1, e2, .envir = parent.frame())
}
#' @importFrom methods setOldClass
setOldClass(c("glue", "character"))
#' Deprecated Functions
#'
#' These functions are Deprecated in this release of glue, they will be removed
#' in a future version.
#' @name glue-deprecated
#' @keywords internal
NULL
glue/R/sql.R 0000644 0001762 0000144 00000012167 13441474521 012345 0 ustar ligges users #' Interpolate strings with SQL escaping
#'
#' SQL databases often have custom quotation syntax for identifiers and strings
#' which make writing SQL queries error prone and cumbersome to do. `glue_sql()` and
#' `glue_data_sql()` are analogs to `glue()` and `glue_data()` which handle the
#' SQL quoting.
#'
#' They automatically quote character results, quote identifiers if the glue
#' expression is surrounded by backticks \sQuote{`} and do not quote
#' non-characters such as numbers. If numeric data is stored in a character
#' column (which should be quoted) pass the data to `glue_sql()` as a
#' character.
#'
#' Returning the result with `DBI::SQL()` will suppress quoting if desired for
#' a given value.
#'
#' Note [parameterized queries](https://db.rstudio.com/best-practices/run-queries-safely#parameterized-queries)
#' are generally the safest and most efficient way to pass user defined
#' values in a query, however not every database driver supports them.
#'
#' If you place a `*` at the end of a glue expression the values will be
#' collapsed with commas. This is useful for the [SQL IN Operator](https://www.w3schools.com/sql/sql_in.asp)
#' for instance.
#' @inheritParams glue
#' @param .con \[`DBIConnection`]:A DBI connection object obtained from `DBI::dbConnect()`.
#' @return A `DBI::SQL()` object with the given query.
#' @examples
#' con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
#' colnames(iris) <- gsub("[.]", "_", tolower(colnames(iris)))
#' DBI::dbWriteTable(con, "iris", iris)
#' var <- "sepal_width"
#' tbl <- "iris"
#' num <- 2
#' val <- "setosa"
#' glue_sql("
#' SELECT {`var`}
#' FROM {`tbl`}
#' WHERE {`tbl`}.sepal_length > {num}
#' AND {`tbl`}.species = {val}
#' ", .con = con)
#'
#' # If sepal_length is store on the database as a character explicitly convert
#' # the data to character to quote appropriately.
#' glue_sql("
#' SELECT {`var`}
#' FROM {`tbl`}
#' WHERE {`tbl`}.sepal_length > {as.character(num)}
#' AND {`tbl`}.species = {val}
#' ", .con = con)
#'
#'
#' # `glue_sql()` can be used in conjuction with parameterized queries using
#' # `DBI::dbBind()` to provide protection for SQL Injection attacks
#' sql <- glue_sql("
#' SELECT {`var`}
#' FROM {`tbl`}
#' WHERE {`tbl`}.sepal_length > ?
#' ", .con = con)
#' query <- DBI::dbSendQuery(con, sql)
#' DBI::dbBind(query, list(num))
#' DBI::dbFetch(query, n = 4)
#' DBI::dbClearResult(query)
#'
#' # `glue_sql()` can be used to build up more complex queries with
#' # interchangeable sub queries. It returns `DBI::SQL()` objects which are
#' # properly protected from quoting.
#' sub_query <- glue_sql("
#' SELECT *
#' FROM {`tbl`}
#' ", .con = con)
#'
#' glue_sql("
#' SELECT s.{`var`}
#' FROM ({sub_query}) AS s
#' ", .con = con)
#'
#' # If you want to input multiple values for use in SQL IN statements put `*`
#' # at the end of the value and the values will be collapsed and quoted appropriately.
#' glue_sql("SELECT * FROM {`tbl`} WHERE sepal_length IN ({vals*})",
#' vals = 1, .con = con)
#'
#' glue_sql("SELECT * FROM {`tbl`} WHERE sepal_length IN ({vals*})",
#' vals = 1:5, .con = con)
#'
#' glue_sql("SELECT * FROM {`tbl`} WHERE species IN ({vals*})",
#' vals = "setosa", .con = con)
#'
#' glue_sql("SELECT * FROM {`tbl`} WHERE species IN ({vals*})",
#' vals = c("setosa", "versicolor"), .con = con)
#'
#' # If you need to reference a table in a different schema use `DBI::Id()` to
#' # construct the identifiers.
#' cols <- c("Sepal.Width", "Sepal.Length", "Species")
#' col_ids <- lapply(cols, function(x) DBI::Id(table="iris", column = x))
#' values <- c(1, 2, 'Setosa')
#' glue_sql("INSERT ({values*}) INTO ({`col_ids`*})", .con=con)
#' @export
glue_sql <- function(..., .con, .envir = parent.frame(), .na = DBI::SQL("NULL")) {
DBI::SQL(glue(..., .envir = .envir, .na = .na, .transformer = sql_quote_transformer(.con)))
}
#' @rdname glue_sql
#' @export
glue_data_sql <- function(.x, ..., .con, .envir = parent.frame(), .na = DBI::SQL("NULL")) {
DBI::SQL(glue_data(.x, ..., .envir = .envir, .na = .na, .transformer = sql_quote_transformer(.con)))
}
sql_quote_transformer <- function(connection) {
function(text, envir) {
should_collapse <- grepl("[*]$", text)
if (should_collapse) {
text <- sub("[*]$", "", text)
}
m <- gregexpr("^`|`$", text)
is_quoted <- any(m[[1]] != -1)
if (is_quoted) {
regmatches(text, m) <- ""
res <- eval(parse(text = text, keep.source = FALSE), envir)
if (length(res) == 1) {
res <- DBI::dbQuoteIdentifier(conn = connection, res)
} else {
# Support lists as well
res[] <- lapply(res, DBI::dbQuoteIdentifier, conn = connection)
}
} else {
res <- eval(parse(text = text, keep.source = FALSE), envir)
# convert objects to characters
if (is.object(res) && !inherits(res, "SQL")) {
res <- as.character(res)
}
# Convert all NA's as needed
if (any(is.na(res))) {
res[is.na(res)] <- NA_character_
}
if(is.character(res)) {
res <- DBI::dbQuoteString(conn = connection, res)
}
}
if (should_collapse) {
res <- glue_collapse(res, ", ")
}
res
}
}
glue/R/color.R 0000644 0001762 0000144 00000004062 13413432727 012660 0 ustar ligges users #' Construct strings with color
#'
#' @description
#' The [crayon][crayon::crayon] package defines a number of functions used to
#' color terminal output. `glue_col()` and `glue_data_col()` functions provide
#' additional syntax to make using these functions in glue strings easier.
#'
#' Using the following syntax will apply the function `blue` function to the text 'foo bar'.
#'
#' ```
#' {blue foo bar}
#' ```
#'
#' If you want an expression to be evaluated, simply place that in a normal brace
#' expression (these can be nested).
#'
#' ```
#' {blue 1 + 1 = {1 + 1}}
#' ```
#'
#' @inheritParams glue
#' @export
#' @examples
#' if (require(crayon)) {
#' glue_col("{blue foo bar}")
#'
#' glue_col("{blue 1 + 1 = {1 + 1}}")
#'
#' white_on_grey <- bgBlack $ white
#' glue_col("{white_on_grey
#' Roses are {red {colors()[[552]]}}
#' Violets are {blue {colors()[[26]]}}
#' `glue_col()` can show {red c}{yellow o}{green l}{cyan o}{blue r}{magenta s}
#' and {bold bold} and {underline underline} too!
#' }")
#' }
glue_col <- function(..., .envir = parent.frame(), .na = "NA") {
loadNamespace("crayon")
glue(..., .envir = .envir, .na = .na, .transformer = color_transformer)
}
#' @rdname glue_col
#' @export
glue_data_col <- function(.x, ..., .envir = parent.frame(), .na = "NA") {
loadNamespace("crayon")
glue_data(.x, ..., .envir = .envir, .na = .na, .transformer = color_transformer)
}
color_transformer <- function(code, envir) {
res <- tryCatch(parse(text = code, keep.source = FALSE), error = function(e) e)
if (!inherits(res, "error")) {
return(eval(res, envir = envir))
}
code <- glue_collapse(code, "\n")
m <- regexpr("(?s)^([[:alnum:]_]+)[[:space:]]+(.+)", code, perl = TRUE)
has_match <- m != -1
if (!has_match) {
stop(res)
}
starts <- attr(m, "capture.start")
ends <- starts + attr(m, "capture.length") - 1L
captures <- substring(code, starts, ends)
fun <- captures[[1]]
text <- captures[[2]]
out <- glue(text, .envir = envir, .transformer = color_transformer)
(get(fun, envir = envir, mode = "function"))(out)
}
glue/R/zzz.R 0000644 0001762 0000144 00000001455 13413432727 012402 0 ustar ligges users # nocov start
.onLoad <- function(...) {
register_s3_method("testthat", "compare", "glue")
invisible()
}
register_s3_method <- function(pkg, generic, class, fun = NULL) {
stopifnot(is.character(pkg), length(pkg) == 1)
stopifnot(is.character(generic), length(generic) == 1)
stopifnot(is.character(class), length(class) == 1)
if (is.null(fun)) {
fun <- get(paste0(generic, ".", class), envir = parent.frame())
} else {
stopifnot(is.function(fun))
}
if (pkg %in% loadedNamespaces()) {
registerS3method(generic, class, fun, envir = asNamespace(pkg))
}
# Always register hook in case package is later unloaded & reloaded
setHook(
packageEvent(pkg, "onLoad"),
function(...) {
registerS3method(generic, class, fun, envir = asNamespace(pkg))
}
)
}
#nocov end
glue/vignettes/ 0000755 0001762 0000144 00000000000 13441546417 013227 5 ustar ligges users glue/vignettes/crayon.html 0000644 0001762 0000144 00000027137 13373633233 015417 0 ustar ligges users
Colored output with crayon
Colored output with crayon
Vignette Author
2018-11-16
The crayon package can be used in conjunction with glue to easily color text output. Simple place the color in between braces {red}
and put a {reset}
when you want to reset the color. This behavior is currently only in the devel version of crayon.
You can also create new objects as combinations of colors.
And calling glue functions directly within {}
works as well
glue/vignettes/releases/ 0000755 0001762 0000144 00000000000 13413432727 015027 5 ustar ligges users glue/vignettes/releases/glue-1.2.0.Rmd 0000644 0001762 0000144 00000011531 13413432727 017064 0 ustar ligges users ---
title: glue 1.2.0
date: '2017-10-31'
slug: glue-1.2.0
author: Jim Hester
categories: [package]
description: >
glue 1.2.0 is now available on CRAN. glue is designed to make it easy to
interpolate ("glue") your data into strings.
photo:
url: http://www.littlerock.af.mil/News/Photos/igphoto/2001697811/
author: Airman 1st Class Grace Nichols
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(
comment = "#>",
collapse = TRUE
)
library(glue)
```
[glue 1.2.0](http://glue.tidyverse.org) is now available on CRAN!
[glue](http://glue.tidyverse.org) is designed to make it easy to interpolate
("glue") your data into strings. Compared to equivalents like `paste()` and
`sprintf()` it is easier to write and less time consuming to maintain. It also
has no non-base dependencies so is easy to include in packages.
Install the latest version with:
```{r, eval = FALSE}
install.packages("glue")
```
glue has three primary functions, `glue()`, `glue_data()` and `collapse()`.
`glue()` works in a similar way to double quotes `"` in a shell or python's
[String Interpolation](https://www.python.org/dev/peps/pep-0498/). You surround
the code you want evaluated by `{}` and the value of the expression is inserted
into the string.
```{r}
name <- "Fred"
age <- 50
anniversary <- as.Date("1991-10-12")
glue('
My name is {name}
my age next year is {age + 1}
my anniversary is {format(anniversary, "%A, %B %d, %Y")}
')
```
glue is also vectorized over its inputs.
```{r}
glue('
{month.abb} is short for {month.name}
')
```
`glue_data()` works like `glue()`, but instead of looking up its variables
from the calling environment it looks them up from the first argument (usually
a data frame or tibble). This makes `glue_data()` very useful within pipe chains.
```{r}
library(magrittr)
mtcars$model <- rownames(mtcars)
mtcars %>%
head %>%
glue_data("The {model} has {gear} gears, {cyl} cylinders, and {hp} horsepower.")
```
`collapse()` is used to combine multiple values into one. The `last` argument
is used to change the separator for the last value.
```{r}
collapse(1:5, ", ", last = ", and ")
```
## glue transformers
New to glue 1.2.0 are transformer functions, which allow you to define custom
behavior for glue functions. For example a `collapse_transformer()` which
automatically collapses any blocks which end with `*`.
```{r}
collapse_transformer <- function(regex = "[*]$", ...) {
function(code, envir) {
if (grepl(regex, code)) {
code <- sub(regex, "", code)
}
res <- eval(parse(text = code), envir = envir)
collapse(res, ...)
}
}
glue("
{1:5*}
{letters[1:5]*}",
.transformer = collapse_transformer(sep = ", ", last = ", and "))
```
Or an sprintf transformer which lets you use sprintf style numeric formatting with glue.
```{r}
sprintf_transformer <- function(code, envir) {
m <- regexpr("%.+$", code)
if (m != -1) {
format <- regmatches(code, m)
regmatches(code, m) <- ""
res <- eval(parse(text = code), envir = envir)
do.call(sprintf, list(format, res))
} else {
eval(parse(text = code), envir = envir)
}
}
glue_fmt <- function(..., .envir = parent.frame()) {
glue(..., .transformer = sprintf_transformer, .envir = .envir)
}
glue_fmt("π = {pi%.5f}")
```
## glue_sql()
Also new to glue 1.2.0 is `glue_sql()` and `glue_data_sql()`, which are helper
functions defined with glue transformers to make it easy and safe to construct
SQL statements.
Using `glue_sql()` values are automatically quoted appropriately and variables
can be quoted with backticks.
```{r}
con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
colnames(iris) <- gsub("[.]", "_", tolower(colnames(iris)))
DBI::dbWriteTable(con, "iris", iris)
var <- "sepal_width"
tbl <- "iris"
num <- 2
val <- "setosa"
glue_sql("
SELECT {`var`}
FROM {`tbl`}
WHERE {`tbl`}.sepal_length > {num}
AND {`tbl`}.species = {val}
", .con = con)
```
## Other changes
There are many other bug fixes and other minor improvements. You can see a
complete list in the [release
notes](https://github.com/tidyverse/glue/releases/tag/v1.2.0).
A big thanks goes to all the community members who contributed code and opened
issues since the last release!
([\@artemklevtsov](https://github.com/artemklevtsov),
[\@daroczig](https://github.com/daroczig),
[\@DarwinAwardWinner](https://github.com/DarwinAwardWinner),
[\@edarague](https://github.com/edarague),
[\@hadley](https://github.com/hadley),
[\@hughjonesd](https://github.com/hughjonesd),
[\@jennybc](https://github.com/jennybc),
[\@jimhester](https://github.com/jimhester),
[\@jjchern](https://github.com/jjchern), [\@klmr](https://github.com/klmr),
[\@krlmlr](https://github.com/krlmlr), [\@lionel-](https://github.com/lionel-),
[\@mgirlich](https://github.com/mgirlich),
[\@mmuurr](https://github.com/mmuurr), [\@npjc](https://github.com/npjc),
[\@pssguy](https://github.com/pssguy), and
[\@robinsones](https://github.com/robinsones))
glue/vignettes/speed.Rmd 0000644 0001762 0000144 00000007056 13413432727 015000 0 ustar ligges users ---
title: "Speed of glue"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Speed of glue}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
% \VignetteDepends{R.utils
R.utils,
forcats,
microbenchmark,
rprintf,
stringr,
ggplot2}
---
Glue is advertised as
> Fast, dependency free string literals
So what do we mean when we say that glue is fast. This does not mean glue is
the fastest thing to use in all cases, however for the features it provides we
can confidently say it is fast.
A good way to determine this is to compare it's speed of execution to some alternatives.
- `base::paste0()`, `base::sprintf()` - Functions in base R implemented in C
that provide variable insertion (but not interpolation).
- `R.utils::gstring()`, `stringr::str_interp()` - Provides a similar interface
as glue, but using `${}` to delimit blocks to interpolate.
- `pystr::pystr_format()`[^1], `rprintf::rprintf()` - Provide a interfaces similar
to python string formatters with variable replacement, but not arbitrary
interpolation.
```{r setup, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE, comment = "#>",
eval = as.logical(Sys.getenv("EVAL_VIGNETTES", "FALSE")),
cache = FALSE)
library(glue)
```
```{r setup2, include = FALSE}
plot_comparison <- function(x, ...) {
library(ggplot2)
library(microbenchmark)
x$expr <- forcats::fct_reorder(x$expr, x$time)
colors <- ifelse(levels(x$expr) == "glue", "orange", "grey")
autoplot(x, ...) +
theme(axis.text.y = element_text(color = colors)) +
aes(fill = expr) + scale_fill_manual(values = colors, guide = FALSE)
}
```
## Simple concatenation
```{r, message = FALSE}
bar <- "baz"
simple <-
microbenchmark::microbenchmark(
glue = glue::glue("foo{bar}"),
gstring = R.utils::gstring("foo${bar}"),
paste0 = paste0("foo", bar),
sprintf = sprintf("foo%s", bar),
str_interp = stringr::str_interp("foo${bar}"),
rprintf = rprintf::rprintf("foo$bar", bar = bar)
)
print(unit = "eps", order = "median", signif = 4, simple)
plot_comparison(simple)
```
While `glue()` is slower than `paste0`,`sprintf()` it is
twice as fast as `str_interp()` and `gstring()`, and on par with `rprintf()`.
`paste0()`, `sprintf()` don't do string interpolation and will likely always be
significantly faster than glue, glue was never meant to be a direct replacement
for them.
`rprintf()` does only variable interpolation, not arbitrary expressions, which
was one of the explicit goals of writing glue.
So glue is ~2x as fast as the two functions (`str_interp()`, `gstring()`) which do have
roughly equivalent functionality.
It also is still quite fast, with over 6000 evaluations per second on this machine.
## Vectorized performance
Taking advantage of glue's vectorization is the best way to avoid performance.
For instance the vectorized form of the previous benchmark is able to generate
100,000 strings in only 22ms with performance much closer to that of
`paste0()` and `sprintf()`. NB. `str_interp()` does not support
vectorization, so were removed.
```{r, message = FALSE}
bar <- rep("bar", 1e5)
vectorized <-
microbenchmark::microbenchmark(
glue = glue::glue("foo{bar}"),
gstring = R.utils::gstring("foo${bar}"),
paste0 = paste0("foo", bar),
sprintf = sprintf("foo%s", bar),
rprintf = rprintf::rprintf("foo$bar", bar = bar)
)
print(unit = "ms", order = "median", signif = 4, vectorized)
plot_comparison(vectorized, log = FALSE)
```
[^1]: pystr is no longer available from CRAN due to failure to correct
installation errors and was therefore removed from further testing.
glue/vignettes/transformers.Rmd 0000644 0001762 0000144 00000011201 13413432727 016410 0 ustar ligges users ---
title: "Transformers"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Transformers}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
Transformers allow you to apply functions to the glue input and output, before
and after evaluation. This allows you to write things like `glue_sql()`, which
automatically quotes variables for you or add a syntax for automatically
collapsing outputs.
The transformer functions simply take two arguments `text` and `envir`, where
`text` is the unparsed string inside the glue block and `envir` is the
execution environment. Most transformers will then call `eval(parse(text = text,
keep.source = FALSE), envir)` which parses and evaluates the code.
You can then supply the transformer function to glue with the `.transformer`
argument. In this way users can define manipulate the text before parsing and
change the output after evaluation.
It is often useful to write a `glue()` wrapper function which supplies a
`.transformer` to `glue()` or `glue_data()` and potentially has additional
arguments. One important consideration when doing this is to include
`.envir = parent.frame()` in the wrapper to ensure the evaluation environment
is correct.
Some examples implementations of potentially useful transformers follow. The
aim right now is not to include most of these custom functions within the
`glue` package. Rather users are encouraged to create custom functions using
transformers to fit their individual needs.
```{r, include = FALSE}
library(glue)
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```
### collapse transformer
A transformer which automatically collapses any glue block ending with `*`.
```{r}
collapse_transformer <- function(regex = "[*]$", ...) {
function(text, envir) {
if (grepl(regex, text)) {
text <- sub(regex, "", text)
}
res <- eval(parse(text = text, keep.source = FALSE), envir)
glue_collapse(res, ...)
}
}
glue("{1:5*}\n{letters[1:5]*}", .transformer = collapse_transformer(sep = ", "))
glue("{1:5*}\n{letters[1:5]*}", .transformer = collapse_transformer(sep = ", ", last = " and "))
```
### Shell quoting transformer
A transformer which automatically quotes variables for use in shell commands,
e.g. via `system()` or `system2()`.
```{r}
shell_transformer <- function(type = c("sh", "csh", "cmd", "cmd2")) {
type <- match.arg(type)
function(text, envir) {
res <- eval(parse(text = text, keep.source = FALSE), envir)
shQuote(res)
}
}
glue_sh <- function(..., .envir = parent.frame(), .type = c("sh", "csh", "cmd", "cmd2")) {
.type <- match.arg(.type)
glue(..., .envir = .envir, .transformer = shell_transformer(.type))
}
filename <- "test"
writeLines(con = filename, "hello!")
command <- glue_sh("cat {filename}")
command
system(command)
```
### emoji transformer
A transformer which converts the text to the equivalent emoji.
```{r, eval = require("emo")}
emoji_transformer <- function(text, envir) {
if (grepl("[*]$", text)) {
text <- sub("[*]$", "", text)
glue_collapse(ji_find(text)$emoji)
} else {
ji(text)
}
}
glue_ji <- function(..., .envir = parent.frame()) {
glue(..., .open = ":", .close = ":", .envir = .envir, .transformer = emoji_transformer)
}
glue_ji("one :heart:")
glue_ji("many :heart*:")
```
### sprintf transformer
A transformer which allows succinct sprintf format strings.
```{r}
sprintf_transformer <- function(text, envir) {
m <- regexpr(":.+$", text)
if (m != -1) {
format <- substring(regmatches(text, m), 2)
regmatches(text, m) <- ""
res <- eval(parse(text = text, keep.source = FALSE), envir)
do.call(sprintf, list(glue("%{format}f"), res))
} else {
eval(parse(text = text, keep.source = FALSE), envir)
}
}
glue_fmt <- function(..., .envir = parent.frame()) {
glue(..., .transformer = sprintf_transformer, .envir = .envir)
}
glue_fmt("π = {pi:.2}")
```
### safely transformer
A transformer that acts like `purrr::safely()`, which returns a value instead of an error.
```{r}
safely_transformer <- function(otherwise = NA) {
function(text, envir) {
tryCatch(
eval(parse(text = text, keep.source = FALSE), envir),
error = function(e) if (is.language(otherwise)) eval(otherwise) else otherwise)
}
}
glue_safely <- function(..., .otherwise = NA, .envir = parent.frame()) {
glue(..., .transformer = safely_transformer(.otherwise), .envir = .envir)
}
# Default returns missing if there is an error
glue_safely("foo: {xyz}")
# Or an empty string
glue_safely("foo: {xyz}", .otherwise = "Error")
# Or output the error message in red
library(crayon)
glue_safely("foo: {xyz}", .otherwise = quote(glue("{red}Error: {conditionMessage(e)}{reset}")))
```
glue/README.md 0000644 0001762 0000144 00000017310 13441541341 012467 0 ustar ligges users
# glue
[](https://cran.r-project.org/package=glue)
[](https://travis-ci.org/tidyverse/glue)
[](https://codecov.io/github/tidyverse/glue?branch=master)
[](https://ci.appveyor.com/project/tidyverse/glue)
## Overview
Glue offers interpreted string literals that are small, fast, and
dependency-free. Glue does this by embedding R expressions in curly
braces which are then evaluated and inserted into the argument string.
## Installation
``` r
# Install the released version from CRAN:
install.packages("glue")
# Install the development version from GitHub:
# install.packages("devtools")
devtools::install_github("tidyverse/glue")
```
## Usage
##### Variables can be passed directly into strings.
``` r
library(glue)
name <- "Fred"
glue('My name is {name}.')
#> My name is Fred.
```
##### Long strings are broken by line and concatenated together.
``` r
library(glue)
name <- "Fred"
age <- 50
anniversary <- as.Date("1991-10-12")
glue('My name is {name},',
' my age next year is {age + 1},',
' my anniversary is {format(anniversary, "%A, %B %d, %Y")}.')
#> My name is Fred, my age next year is 51, my anniversary is Saturday, October 12, 1991.
```
##### Named arguments are used to assign temporary variables.
``` r
glue('My name is {name},',
' my age next year is {age + 1},',
' my anniversary is {format(anniversary, "%A, %B %d, %Y")}.',
name = "Joe",
age = 40,
anniversary = as.Date("2001-10-12"))
#> My name is Joe, my age next year is 41, my anniversary is Friday, October 12, 2001.
```
##### `glue_data()` is useful with [magrittr](https://cran.r-project.org/package=magrittr) pipes.
``` r
`%>%` <- magrittr::`%>%`
head(mtcars) %>% glue_data("{rownames(.)} has {hp} hp")
#> Mazda RX4 has 110 hp
#> Mazda RX4 Wag has 110 hp
#> Datsun 710 has 93 hp
#> Hornet 4 Drive has 110 hp
#> Hornet Sportabout has 175 hp
#> Valiant has 105 hp
```
##### Or within dplyr pipelines
``` r
library(dplyr)
head(iris) %>%
mutate(description = glue("This {Species} has a petal length of {Petal.Length}"))
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1 5.1 3.5 1.4 0.2 setosa
#> 2 4.9 3.0 1.4 0.2 setosa
#> 3 4.7 3.2 1.3 0.2 setosa
#> 4 4.6 3.1 1.5 0.2 setosa
#> 5 5.0 3.6 1.4 0.2 setosa
#> 6 5.4 3.9 1.7 0.4 setosa
#> description
#> 1 This setosa has a petal length of 1.4
#> 2 This setosa has a petal length of 1.4
#> 3 This setosa has a petal length of 1.3
#> 4 This setosa has a petal length of 1.5
#> 5 This setosa has a petal length of 1.4
#> 6 This setosa has a petal length of 1.7
```
##### Leading whitespace and blank lines from the first and last lines are automatically trimmed.
This lets you indent the strings naturally in code.
``` r
glue("
A formatted string
Can have multiple lines
with additional indention preserved
")
#> A formatted string
#> Can have multiple lines
#> with additional indention preserved
```
##### An additional newline can be used if you want a leading or trailing newline.
``` r
glue("
leading or trailing newlines can be added explicitly
")
#>
#> leading or trailing newlines can be added explicitly
```
##### `\\` at the end of a line continues it without a new line.
``` r
glue("
A formatted string \\
can also be on a \\
single line
")
#> A formatted string can also be on a single line
```
##### A literal brace is inserted by using doubled braces.
``` r
name <- "Fred"
glue("My name is {name}, not {{name}}.")
#> My name is Fred, not {name}.
```
##### Alternative delimiters can be specified with `.open` and `.close`.
``` r
one <- "1"
glue("The value of $e^{2\\pi i}$ is $<>$.", .open = "<<", .close = ">>")
#> The value of $e^{2\pi i}$ is $1$.
```
##### All valid R code works in expressions, including braces and escaping.
Backslashes do need to be doubled just like in all R strings.
``` r
`foo}\`` <- "foo"
glue("{
{
'}\\'' # { and } in comments, single quotes
\"}\\\"\" # or double quotes are ignored
`foo}\\`` # as are { in backticks
}
}")
#> foo
```
##### `glue_sql()` makes constructing SQL statements safe and easy
Use backticks to quote identifiers, normal strings and numbers are
quoted appropriately for your backend.
``` r
library(glue)
con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
colnames(iris) <- gsub("[.]", "_", tolower(colnames(iris)))
DBI::dbWriteTable(con, "iris", iris)
var <- "sepal_width"
tbl <- "iris"
num <- 2
val <- "setosa"
glue_sql("
SELECT {`var`}
FROM {`tbl`}
WHERE {`tbl`}.sepal_length > {num}
AND {`tbl`}.species = {val}
", .con = con)
#> SELECT `sepal_width`
#> FROM `iris`
#> WHERE `iris`.sepal_length > 2
#> AND `iris`.species = 'setosa'
# `glue_sql()` can be used in conjunction with parameterized queries using
# `DBI::dbBind()` to provide protection for SQL Injection attacks
sql <- glue_sql("
SELECT {`var`}
FROM {`tbl`}
WHERE {`tbl`}.sepal_length > ?
", .con = con)
query <- DBI::dbSendQuery(con, sql)
DBI::dbBind(query, list(num))
DBI::dbFetch(query, n = 4)
#> sepal_width
#> 1 3.5
#> 2 3.0
#> 3 3.2
#> 4 3.1
DBI::dbClearResult(query)
# `glue_sql()` can be used to build up more complex queries with
# interchangeable sub queries. It returns `DBI::SQL()` objects which are
# properly protected from quoting.
sub_query <- glue_sql("
SELECT *
FROM {`tbl`}
", .con = con)
glue_sql("
SELECT s.{`var`}
FROM ({sub_query}) AS s
", .con = con)
#> SELECT s.`sepal_width`
#> FROM (SELECT *
#> FROM `iris`) AS s
# If you want to input multiple values for use in SQL IN statements put `*`
# at the end of the value and the values will be collapsed and quoted appropriately.
glue_sql("SELECT * FROM {`tbl`} WHERE sepal_length IN ({vals*})",
vals = 1, .con = con)
#> SELECT * FROM `iris` WHERE sepal_length IN (1)
glue_sql("SELECT * FROM {`tbl`} WHERE sepal_length IN ({vals*})",
vals = 1:5, .con = con)
#> SELECT * FROM `iris` WHERE sepal_length IN (1, 2, 3, 4, 5)
glue_sql("SELECT * FROM {`tbl`} WHERE species IN ({vals*})",
vals = "setosa", .con = con)
#> SELECT * FROM `iris` WHERE species IN ('setosa')
glue_sql("SELECT * FROM {`tbl`} WHERE species IN ({vals*})",
vals = c("setosa", "versicolor"), .con = con)
#> SELECT * FROM `iris` WHERE species IN ('setosa', 'versicolor')
```
##### Optionally combine strings with `+`
``` r
x <- 1
y <- 3
glue("x + y") + " = {x + y}"
#> x + y = 4
```
# Other implementations
Some other implementations of string interpolation in R (although not
using identical
syntax).
- [stringr::str\_interp](http://stringr.tidyverse.org/reference/str_interp.html)
- [pystr::pystr\_format](https://cran.r-project.org/package=pystr)
- [R.utils::gstring](https://cran.r-project.org/package=R.utils)
- [rprintf](https://cran.r-project.org/package=rprintf)
String templating is closely related to string interpolation, although
not exactly the same concept. Some packages implementing string
templating in R include.
- [whisker](https://cran.r-project.org/package=whisker)
- [brew](https://cran.r-project.org/package=brew)
glue/MD5 0000644 0001762 0000144 00000004350 13442031352 011515 0 ustar ligges users e249649309a71c9b9aadaaa20c82fdf1 *DESCRIPTION
e2965db868cda3b9ce7b138d8ca0e6bc *LICENSE
ab0bab7e960d17745b5c42ea2851fa78 *NAMESPACE
9d5f6738007960360abc11d37d0d9df8 *NEWS.md
23633d4a07712b86af2180068ac71389 *R/color.R
01aa91257d77723243ebfa48a7452607 *R/glue.R
60296b4b4d13ac8137964a6e169daf33 *R/knitr.R
3d8aeead42b31c43e432a6ea7f5b0bf7 *R/quoting.R
ac268ef4408a389392b755d98cda7475 *R/sql.R
6165b52f7c742eb23db02a3e0b43a8d8 *R/transformer.R
f90ae85785985dc3dbb1c4c9673dedae *R/utils.R
37de69d5d3d7b7cf0ee12eb6108b0c75 *R/zzz.R
f4309efa623f76e32eca1ed477898374 *README.md
2101d9d107ef911cd126818dafc25311 *build/vignette.rds
c47f1ae270e7895d321d901edf001d1b *inst/doc/speed.R
4f7146b3f17052f82dbb3a8c168139cc *inst/doc/speed.Rmd
41f124316b1cf972400efcca81be9865 *inst/doc/speed.html
bca899898fc602139bd60891c4a565f2 *inst/doc/transformers.R
450e385bccdd45975ed0b919f6a0d37b *inst/doc/transformers.Rmd
11e0fdfd8e510e116bf7ae6d7210e80e *inst/doc/transformers.html
428090ffb747ed472a50927771a36cce *man/as_glue.Rd
ac7e2258d7284d18ab570e161fa682d0 *man/figures/logo.png
0bbc5c39d627b1343f1f64f5f7fe0ac2 *man/glue-deprecated.Rd
2c6fc077033e01725824e9c192052237 *man/glue.Rd
35c4d833611dffab33a5577f94868876 *man/glue_col.Rd
9aae18528ff53af751c5d073b3924111 *man/glue_collapse.Rd
d466c72ebcda7d6595a533847d72a578 *man/glue_sql.Rd
a362a9409917bcd234edbd1679f31a96 *man/identity_transformer.Rd
38ada418386488d36bb7e925f6e30326 *man/quoting.Rd
4d9003660e95938f16e32c0ccf290507 *man/trim.Rd
ec2a0b7fa83ed87d01f9223f7a7686f5 *src/glue.c
57c4e91a5ecf31acbbc061d6650584bb *src/init.c
3847130cfb06fd215e1c8228cd262892 *src/trim.c
2b2d5c82e65ffac3ce2300a7ba32fa68 *tests/testthat.R
7d1ccbfc509b042d851ca6e2d6ec2553 *tests/testthat/test-collapse.R
232a7a98cd7b9577172e5fca812be0ec *tests/testthat/test-color.R
7233ba07b8b7be5cdd1cdef57e54352b *tests/testthat/test-glue.R
05436a79623c08d7daa419af36fe110e *tests/testthat/test-quoting.R
0ae9251c30c2fbf800e6a7fb7483cd40 *tests/testthat/test-sql.R
16474c3bf9ab22ef1fade80eae27a847 *tests/testthat/test-trim.R
21c44b01b750f96cfb4bcd3d5c84ba84 *vignettes/crayon.html
d962c605a4070ebee55aebfc27118c36 *vignettes/releases/glue-1.2.0.Rmd
4f7146b3f17052f82dbb3a8c168139cc *vignettes/speed.Rmd
450e385bccdd45975ed0b919f6a0d37b *vignettes/transformers.Rmd
glue/build/ 0000755 0001762 0000144 00000000000 13441546415 012314 5 ustar ligges users glue/build/vignette.rds 0000644 0001762 0000144 00000000341 13441546415 014651 0 ustar ligges users uM0˯B` 'ƍ.6RԄ5ĝ':5Eu^/!!&gہIb]<;V9XrZBqFC|243 wYH6u`Z
ڌ$Q&2pwdմb:=J?;l-ge-srgAoLdo(0Dג
H0f?s glue/DESCRIPTION 0000644 0001762 0000144 00000002147 13442031352 012715 0 ustar ligges users Package: glue
Title: Interpreted String Literals
Version: 1.3.1
Authors@R: person("Jim", "Hester", email = "james.f.hester@gmail.com", role = c("aut", "cre"))
Description: An implementation of interpreted string literals, inspired by
Python's Literal String Interpolation and Docstrings
and Julia's Triple-Quoted String Literals
.
Depends: R (>= 3.1)
Imports: methods
Suggests: testthat, covr, magrittr, crayon, knitr, rmarkdown, DBI,
RSQLite, R.utils, forcats, microbenchmark, rprintf, stringr,
ggplot2, dplyr, withr
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
RoxygenNote: 6.1.1
URL: https://github.com/tidyverse/glue
BugReports: https://github.com/tidyverse/glue/issues
VignetteBuilder: knitr
ByteCompile: true
NeedsCompilation: yes
Packaged: 2019-03-11 21:03:11 UTC; jhester
Author: Jim Hester [aut, cre]
Maintainer: Jim Hester
Repository: CRAN
Date/Publication: 2019-03-12 22:30:02 UTC
glue/man/ 0000755 0001762 0000144 00000000000 13441467711 011771 5 ustar ligges users glue/man/as_glue.Rd 0000644 0001762 0000144 00000000462 13326630265 013677 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/glue.R
\name{as_glue}
\alias{as_glue}
\title{Coerce object to glue}
\usage{
as_glue(x, ...)
}
\arguments{
\item{x}{object to be coerced.}
\item{...}{further arguments passed to methods.}
}
\description{
Coerce object to glue
}
glue/man/quoting.Rd 0000644 0001762 0000144 00000001037 13413432727 013745 0 ustar ligges users % Generated by roxygen2: do not edit by hand
% Please edit documentation in R/quoting.R
\name{quoting}
\alias{quoting}
\alias{single_quote}
\alias{double_quote}
\alias{backtick}
\title{Quoting operators}
\usage{
single_quote(x)
double_quote(x)
backtick(x)
}
\arguments{
\item{x}{A character to quote.}
}
\description{
These functions make it easy to quote each individual element and are useful
in conjunction with \code{glue_collapse()}.
}
\examples{
x <- 1:5
glue('Values of x: {glue_collapse(backtick(x), sep = ", ", last = " and ")}')
}
glue/man/figures/ 0000755 0001762 0000144 00000000000 13441464215 013431 5 ustar ligges users glue/man/figures/logo.png 0000644 0001762 0000144 00000135473 13441464215 015114 0 ustar ligges users PNG
IHDR X? gAMA a cHRM z&