diff --git a/README.Rmd b/README.Rmd index 9284827..d50991c 100644 --- a/README.Rmd +++ b/README.Rmd @@ -1,7 +1,5 @@ --- -output: - github_document: - html_preview: false +output: github_document --- @@ -45,14 +43,20 @@ devtools::install_github("tidyverse/glue") ## Usage -##### Variables can be passed directly into strings. +`glue()` makes it easy to interpolate data into strings: + ```{r} library(glue) + name <- "Fred" glue('My name is {name}.') + +# A literal brace is inserted by using doubled braces. +name <- "Fred" +glue("My name is {name}, not {{name}}.") ``` -Note that `glue::glue()` is also made available via `stringr::str_glue()`. +`glue::glue()` is also made available via `stringr::str_glue()`. So if you've already attached stringr (or perhaps the whole tidyverse), you can access `glue()` like so: ```{r eval = FALSE} @@ -65,104 +69,22 @@ str_glue('{stringr_fcn} is essentially an alias for {glue_fcn}.') #> `stringr::str_glue()` is essentially an alias for `glue::glue()`. ``` -##### 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")}.') -``` - -##### 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")) -``` - -##### `glue_data()` is useful with [magrittr](https://cran.r-project.org/package=magrittr) pipes. -```{r} -`%>%` <- magrittr::`%>%` -head(mtcars) %>% glue_data("{rownames(.)} has {hp} hp") -``` - -##### Or within dplyr pipelines -```{r, message = FALSE} -library(dplyr) -head(iris) %>% - mutate(description = glue("This {Species} has a petal length of {Petal.Length}")) -``` - -##### 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 - ") -``` - -##### An additional newline can be used if you want a leading or trailing newline. -```{r} -glue(" - - 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 literal brace is inserted by using doubled braces. -```{r} -name <- "Fred" -glue("My name is {name}, not {{name}}.") -``` +`glue_data()` works well with pipes: -##### Alternative delimiters can be specified with `.open` and `.close`. ```{r} -one <- "1" -glue("The value of $e^{2\\pi i}$ is $<>$.", .open = "<<", .close = ">>") +mtcars$model <- rownames(mtcars) +mtcars |> head() |> glue_data("{model} has {hp} hp") ``` -##### 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 - } - }") -``` - -##### `glue_sql()` makes constructing SQL statements safe and easy +`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 @@ -173,54 +95,9 @@ glue_sql(" WHERE {`tbl`}.sepal_length > {num} AND {`tbl`}.species = {val} ", .con = con) - -# `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) -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) ``` -##### Optionally combine strings with `+` - -```{r} -x <- 1 -y <- 3 -glue("x + y") + " = {x + y}" -``` +Learn more in `vignette("glue")`. # Other implementations diff --git a/README.md b/README.md index 7dae217..58609ca 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/glue)](https://cran.r-project.org/package=glue) -[![R-CMD-check](https://github.com/tidyverse/glue/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/tidyverse/glue/actions/workflows/R-CMD-check.yaml) +[![R-CMD-check](https://github.com/tidyverse/glue/actions/workflows/.github/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/tidyverse/glue/actions/workflows/.github/workflows/R-CMD-check.yaml) [![test-coverage](https://github.com/tidyverse/glue/actions/workflows/test-coverage.yaml/badge.svg)](https://github.com/tidyverse/glue/actions/workflows/test-coverage.yaml) @@ -38,18 +38,24 @@ devtools::install_github("tidyverse/glue") ## Usage -##### Variables can be passed directly into strings. +`glue()` makes it easy to interpolate data into strings: ``` r library(glue) + name <- "Fred" glue('My name is {name}.') #> My name is Fred. + +# A literal brace is inserted by using doubled braces. +name <- "Fred" +glue("My name is {name}, not {{name}}.") +#> My name is Fred, not {name}. ``` -Note that `glue::glue()` is also made available via -`stringr::str_glue()`. So if you’ve already attached stringr (or perhaps -the whole tidyverse), you can access `glue()` like so: +`glue::glue()` is also made available via `stringr::str_glue()`. So if +you’ve already attached stringr (or perhaps the whole tidyverse), you +can access `glue()` like so: ``` r library(stringr) # or library(tidyverse) @@ -61,37 +67,11 @@ str_glue('{stringr_fcn} is essentially an alias for {glue_fcn}.') #> `stringr::str_glue()` is essentially an alias for `glue::glue()`. ``` -##### Long strings are broken by line and concatenated together. +`glue_data()` works well with pipes: ``` 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") +mtcars$model <- rownames(mtcars) +mtcars |> head() |> glue_data("{model} has {hp} hp") #> Mazda RX4 has 110 hp #> Mazda RX4 Wag has 110 hp #> Datsun 710 has 93 hp @@ -100,109 +80,15 @@ head(mtcars) %>% glue_data("{rownames(.)} has {hp} hp") #> Valiant has 105 hp ``` -##### Or within dplyr pipelines +`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(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 @@ -217,84 +103,26 @@ glue_sql(" #> 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 -``` +Learn more in `vignette("glue")`. # Other implementations Some other implementations of string interpolation in R (although not using identical syntax). -- [stringr::str_interp](https://stringr.tidyverse.org/reference/str_interp.html) -- [R.utils::gstring](https://cran.r-project.org/package=R.utils) -- [rprintf](https://cran.r-project.org/package=rprintf) +- [stringr::str_interp](https://stringr.tidyverse.org/reference/str_interp.html) +- [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) -- [jinjar](https://cran.r-project.org/package=jinjar) +- [whisker](https://cran.r-project.org/package=whisker) +- [brew](https://cran.r-project.org/package=brew) +- [jinjar](https://cran.r-project.org/package=jinjar) ## Code of Conduct diff --git a/vignettes/glue.Rmd b/vignettes/glue.Rmd new file mode 100644 index 0000000..af65212 --- /dev/null +++ b/vignettes/glue.Rmd @@ -0,0 +1,158 @@ +--- +title: "Getting started with glue" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Get started with glue} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set(collapse = TRUE, comment = "#>") +``` + +The `glue` package contains functions for glueing together character strings and R code. + +```{r} +library(glue) +``` + +## Gluing and interpolating + +`glue()` can be used to glue together character strings, such as text: + +```{r} +glue("glueing ", "some ", "text ", "together") +``` + +But it's real power comes with `{}`: anything inside of `{}` will be evaluated and pasted into the string. +This makes it easy to interpolate variables: + +```{r} +name <- "glue" +glue('We are learning how to use the {name} R package.') +``` + +And more complex functions: + +```{r} +release_date <- as.Date("2017-06-13") +glue('The first version of the glue package was released on a {format(release_date, "%A")}.') +``` + +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 + } + }") +``` + +## Long lines + +Glue comes with a number of features that make it easier to use when work with large quantities of text. +Leading whitespace and blank lines from the first and last lines are automatically trimmed with `glue()`, letting you indent the strings naturally in code: + +```{r} +my_fun <- function() { + glue(" + A formatted string + Can have multiple lines + with additional indention preserved + ") +} +my_fun() +``` + +An add extra newlines can be used if you want a leading or trailing newline: + +```{r} +glue(" + + leading or trailing newlines can be added explicitly + + ") +``` + +You can use `\\` at the end of a line continues to continue a single line: + +```{r} +glue(" + A formatted string \\ + can also be on a \\ + single line + ") +``` + +## SQL + +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} +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) +``` + +`glue_sql()` can be used in conjunction with parameterized queries using `DBI::dbBind()` to provide protection for SQL Injection attacks + +```{r} +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. + +```{r} +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. + +```{r} +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) +```