From 4bb89cbea35ba3b7f1c16a0dfcc18da4239475b1 Mon Sep 17 00:00:00 2001 From: Jenny Bryan Date: Fri, 23 Aug 2024 14:07:24 -0700 Subject: [PATCH 1/8] Work on README --- README.Rmd | 150 +++++++++++----------------------- README.md | 235 ++++++++++++++++++----------------------------------- 2 files changed, 128 insertions(+), 257 deletions(-) diff --git a/README.Rmd b/README.Rmd index 1b7402c..4a7c392 100644 --- a/README.Rmd +++ b/README.Rmd @@ -21,9 +21,8 @@ library(glue) [![test-coverage](https://github.com/tidyverse/glue/actions/workflows/test-coverage.yaml/badge.svg)](https://github.com/tidyverse/glue/actions/workflows/test-coverage.yaml) -## 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. +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 string. ## Installation @@ -49,136 +48,85 @@ pak::pak("tidyverse/glue") 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}}.") +glue("My name is {name}.") ``` -`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: +`stringr::str_glue()` is an alias for `glue::glue()`. +So if you've already attached stringr (or perhaps the whole tidyverse), you can use `str_glue()` to access all of the functionality of `glue()`: ```{r eval = FALSE} library(stringr) # or library(tidyverse) -stringr_fcn <- "`stringr::str_glue()`" -glue_fcn <- "`glue::glue()`" - -str_glue('{stringr_fcn} is essentially an alias for {glue_fcn}.') -#> `stringr::str_glue()` is essentially an alias for `glue::glue()`. +name <- "Wilma" +str_glue("My name is {name}.") +#> My name is Wilma. ``` -`glue_data()` works well with pipes: +You're not limited to using a bare symbol inside `{}`; it can basically be any little bit of R code: ```{r} -mtcars$model <- rownames(mtcars) -mtcars |> head() |> glue_data("{model} has {hp} hp") +name <- "Pebbles" +glue("Here is my name in uppercase and doubled: {strrep(toupper(name), 2)}.") ``` -##### `glue_data()` is useful with [magrittr](https://cran.r-project.org/package=magrittr) pipes. -```{r} -`%>%` <- magrittr::`%>%` -head(mtcars) %>% glue_data("{rownames(.)} has {hp} hp") -``` +### Data can come from various sources -##### `glue()` is useful within dplyr pipelines -```{r, message = FALSE} -library(dplyr) -head(iris) %>% - mutate(description = glue("This {Species} has a petal length of {Petal.Length}")) -``` +glue can interpolate values from the local environment or from data passed in `name = value` form: -##### 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 - ") +x <- "the local environment" +glue( + "`glue()` can access values from {x} or from {y}. {z}", + y = "named arguments", + z = "Woo!" +) ``` -##### An additional newline can be used if you want a leading or trailing newline. -```{r} -glue(" - - leading or trailing newlines can be added explicitly +If the relevant data lives in a data frame (or list or environment), use `glue_data()` instead: - ") -``` - -##### `\\` at the end of a line continues it without a new line. ```{r} -glue(" - A formatted string \\ - can also be on a \\ - single line - ") +mini_mtcars <- head(cbind(model = rownames(mtcars), mtcars)) +glue_data(mini_mtcars, "{model} has {hp} hp.") ``` -##### A literal brace is inserted by using doubled braces. -```{r} -name <- "Fred" -glue("My name is {name}, not {{name}}.") -``` +`glue_data()` is very natural to use with the pipe: -##### Alternative delimiters can be specified with `.open` and `.close`. ```{r} -one <- "1" -glue("The value of $e^{2\\pi i}$ is $<>$.", .open = "<<", .close = ">>") +mini_mtcars |> + glue_data("{model} gets {mpg} miles per gallon.") ``` -##### 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 - } - }") -``` +These `glue_data()` examples also demonstrate that `glue()` is vectorized over the data. -##### `glue_sql()` makes constructing SQL statements safe and easy -Use backticks to quote identifiers, normal strings and numbers are quoted -appropriately for your backend. +### What you see is awfully close to what you get -```{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()` lets you write code that makes it easy to predict what the final string will look like. +There is considerably less syntactical noise and mystery, compared to `paste()` and `sprintf()`, for example. -# Other implementations +Empty first and last lines are automatically trimmed, as is leading whitespace that is common across all lines. +You don't have to choose between indenting your code properly and getting the output you actually want. -Some other implementations of string interpolation in R (although not using identical syntax). +```{r} +if (TRUE) { + glue(" + A formatted string + Can have multiple lines + with additional indention preserved + " + ) +} +``` -- [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) +## Learning more -String templating is closely related to string interpolation, although not -exactly the same concept. Some packages implementing string templating in R -include. +glue is a relatively small and focused package, but there's more to it than the basic usage of `glue()` and `glue_data()` shown here. +More recommended functions and resources: -- [whisker](https://cran.r-project.org/package=whisker) -- [brew](https://cran.r-project.org/package=brew) -- [jinjar](https://cran.r-project.org/package=jinjar) +* The "Get started" article (`vignette("glue", package = "glue")`) demonstrates more interesting features of `glue()` and `glue_data()`. +* `glue_sql()` and `glue_data_sql()` are specialized functions for producing SQL statements. +* glue provides a couple of custom knitr engines that allow you to use glue syntax in chunks; learn more in `vignette("engines", package = "glue")`. ## Code of Conduct -Please note that the glue project is released with a [Contributor Code of Conduct](https://glue.tidyverse.org/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. +Please note that this project is released with a [Contributor Code of Conduct](https://glue.tidyverse.org/CODE_OF_CONDUCT.html). +By participating in this project, you agree to abide by its terms. diff --git a/README.md b/README.md index 53c21f4..5f17e2c 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,9 @@ [![test-coverage](https://github.com/tidyverse/glue/actions/workflows/test-coverage.yaml/badge.svg)](https://github.com/tidyverse/glue/actions/workflows/test-coverage.yaml) -## 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. +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 string. ## Installation @@ -44,192 +42,117 @@ pak::pak("tidyverse/glue") library(glue) name <- "Fred" -glue('My name is {name}.') +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}. ``` -`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: +`stringr::str_glue()` is an alias for `glue::glue()`. So if you’ve +already attached stringr (or perhaps the whole tidyverse), you can use +`str_glue()` to access all of the functionality of `glue()`: ``` r library(stringr) # or library(tidyverse) -stringr_fcn <- "`stringr::str_glue()`" -glue_fcn <- "`glue::glue()`" - -str_glue('{stringr_fcn} is essentially an alias for {glue_fcn}.') -#> `stringr::str_glue()` is essentially an alias for `glue::glue()`. -``` - -`glue_data()` works well with pipes: - -``` r -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 -#> Hornet 4 Drive has 110 hp -#> Hornet Sportabout has 175 hp -#> Valiant has 105 hp -``` - -##### `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 -``` - -##### `glue()` is useful 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 +name <- "Wilma" +str_glue("My name is {name}.") +#> My name is Wilma. ``` -##### Leading whitespace and blank lines from the first and last lines are automatically trimmed. - -This lets you indent the strings naturally in code. +You’re not limited to using a bare symbol inside `{}`; it can basically +be any little bit of R 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 +name <- "Pebbles" +glue("Here is my name in uppercase and doubled: {strrep(toupper(name), 2)}.") +#> Here is my name in uppercase and doubled: PEBBLESPEBBLES. ``` -##### 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 -``` +### Data can come from various sources -##### `\\` at the end of a line continues it without a new line. +glue can interpolate values from the local environment or from data +passed in `name = value` form: ``` r -glue(" - A formatted string \\ - can also be on a \\ - single line - ") -#> A formatted string can also be on a single line +x <- "the local environment" +glue( + "`glue()` can access values from {x} or from {y}. {z}", + y = "named arguments", + z = "Woo!" +) +#> `glue()` can access values from the local environment or from named arguments. Woo! ``` -##### A literal brace is inserted by using doubled braces. +If the relevant data lives in a data frame (or list or environment), use +`glue_data()` instead: ``` r -name <- "Fred" -glue("My name is {name}, not {{name}}.") -#> My name is Fred, not {name}. +mini_mtcars <- head(cbind(model = rownames(mtcars), mtcars)) +glue_data(mini_mtcars, "{model} 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. ``` -##### Alternative delimiters can be specified with `.open` and `.close`. +`glue_data()` is very natural to use with the pipe: ``` r -one <- "1" -glue("The value of $e^{2\\pi i}$ is $<>$.", .open = "<<", .close = ">>") -#> The value of $e^{2\pi i}$ is $1$. +mini_mtcars |> + glue_data("{model} gets {mpg} miles per gallon.") +#> Mazda RX4 gets 21 miles per gallon. +#> Mazda RX4 Wag gets 21 miles per gallon. +#> Datsun 710 gets 22.8 miles per gallon. +#> Hornet 4 Drive gets 21.4 miles per gallon. +#> Hornet Sportabout gets 18.7 miles per gallon. +#> Valiant gets 18.1 miles per gallon. ``` -##### All valid R code works in expressions, including braces and escaping. - -Backslashes do need to be doubled just like in all R strings. +These `glue_data()` examples also demonstrate that `glue()` is +vectorized over the data. -``` r - `foo}\`` <- "foo" -glue("{ - { - '}\\'' # { and } in comments, single quotes - \"}\\\"\" # or double quotes are ignored - `foo}\\`` # as are { in backticks - } - }") -#> foo -``` +### What you see is awfully close to what you get -##### `glue_sql()` makes constructing SQL statements safe and easy +`glue()` lets you write code that makes it easy to predict what the +final string will look like. There is considerably less syntactical +noise and mystery, compared to `paste()` and `sprintf()`, for example. -Use backticks to quote identifiers, normal strings and numbers are -quoted appropriately for your backend. +Empty first and last lines are automatically trimmed, as is leading +whitespace that is common across all lines. You don’t have to choose +between indenting your code properly and getting the output you actually +want. ``` 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) -#> SELECT `sepal_width` -#> FROM `iris` -#> WHERE `iris`.sepal_length > 2 -#> AND `iris`.species = 'setosa' +if (TRUE) { + glue(" + A formatted string + Can have multiple lines + with additional indention preserved + " + ) +} +#> A formatted string +#> Can have multiple lines +#> with additional indention preserved ``` -# 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) +## Learning more -String templating is closely related to string interpolation, although -not exactly the same concept. Some packages implementing string -templating in R include. +glue is a relatively small and focused package, but there’s more to it +than the basic usage of `glue()` and `glue_data()` shown here. More +recommended functions and resources: -- [whisker](https://cran.r-project.org/package=whisker) -- [brew](https://cran.r-project.org/package=brew) -- [jinjar](https://cran.r-project.org/package=jinjar) +- The “Get started” article (`vignette("glue", package = "glue")`) + demonstrates more interesting features of `glue()` and `glue_data()`. +- `glue_sql()` and `glue_data_sql()` are specialized functions for + producing SQL statements. +- glue provides a couple of custom knitr engines that allow you to use + glue syntax in chunks; learn more in + `vignette("engines", package = "glue")`. ## Code of Conduct -Please note that the glue project is released with a [Contributor Code -of Conduct](https://glue.tidyverse.org/CODE_OF_CONDUCT.html). By -contributing to this project, you agree to abide by its terms. +Please note that this project is released with a [Contributor Code of +Conduct](https://glue.tidyverse.org/CODE_OF_CONDUCT.html). By +participating in this project, you agree to abide by its terms. From a502eb3459d618feec8a803fd54f0643d450d37b Mon Sep 17 00:00:00 2001 From: Jenny Bryan Date: Tue, 27 Aug 2024 07:56:23 -0700 Subject: [PATCH 2/8] Work on "Get started" vignette --- vignettes/glue.Rmd | 217 +++++++++++++++++++++++++++++---------------- 1 file changed, 140 insertions(+), 77 deletions(-) diff --git a/vignettes/glue.Rmd b/vignettes/glue.Rmd index 8aec051..12d9758 100644 --- a/vignettes/glue.Rmd +++ b/vignettes/glue.Rmd @@ -1,23 +1,20 @@ --- -title: "Getting started with glue" +title: "Introduction to glue" output: rmarkdown::html_vignette vignette: > - %\VignetteIndexEntry{Get started with glue} + %\VignetteIndexEntry{Introduction to glue} %\VignetteEncoding{UTF-8} %\VignetteEngine{knitr::rmarkdown} -editor_options: - markdown: - wrap: 72 +editor_options: + markdown: + wrap: sentence --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` -The glue package contains functions for string interpolation: glueing -together character strings and R code. - -To begin, we'll need to load the package. +The glue package contains functions for string interpolation: gluing together character strings and R code. ```{r} library(glue) @@ -25,116 +22,186 @@ library(glue) ## Gluing and interpolating -`glue()` can be used to glue together character strings, such as text: +`glue()` can be used to glue together some pieces of text: ```{r} -glue("glueing ", "some ", "text ", "together") +glue("glue ", "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: +But glue's real power comes with `{}`: anything inside of `{}` is 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.') +glue("We are learning how to use the {name} R package.") ``` -And more complex functions: +As well as more complex expressions: ```{r} release_date <- as.Date("2017-06-13") -glue('The first version of the glue package was released on a {format(release_date, "%A")}.') +glue("Release was 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. -Consider, for instance, adding a variable named `` foo}` `` and printing -its value: - -```{r} -`foo}\`` <- "My value" -glue("{ - `foo}\\`` - }") +## Control of line breaks -``` +`glue()` honors the line breaks in its input: ```{r} -`foo}\`` <- "My value" -glue("{ - { - '}\\'' # { and } in comments, single quotes - \"}\\\"\" # or double quotes are ignored - `foo}\\`` # as are { in backticks - } - }") +glue(" + A formatted string + Can have multiple lines + with additional indention preserved + " +) ``` -## Long lines of text +The example above demonstrates some other important facts about the pre-processing of the template string: + +- An empty first or last line is automatically trimmed. +- Leading whitespace that is common across all lines is trimmed. -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: +The elimination of common leading whitespace is advantageous, because you aren't forced to choose between indenting your code normally and getting the output you actually want. +This is easier to appreciate when you have `glue()` inside a function body (this example also shows an alternative way of styling the end of a `glue()` call): ```{r} -my_fun <- function() { +foo <- function() { glue(" A formatted string Can have multiple lines - with additional indention preserved - ") + with additional indention preserved") } -my_fun() +foo() ``` -An add extra newlines can be used if you want a leading or trailing -newline: +On the other hand, what if you don't want a line break in the output, but you also like to limit the length of lines in your source code to, e.g., 80 characters? +The first option is to use `\\` to break the template string into multiple lines, without getting line breaks in the output: ```{r} +release_date <- as.Date("2017-06-13") glue(" + The first version of the glue package was released on \\ + a {format(release_date, '%A')}.") +``` + +This comes up fairly often when an expression to evaluate inside `{}` takes up more characters than its result, i.e. `format(release_date, '%A')` versus `Tuesday`. +A second way to achieve the same result is to break the template into individual pieces, which are then concatenated. + +```{r} +glue( + "The first version of the glue package was released on ", + "a {format(release_date, '%A')}." +) +``` + +If you want an explicit newline at the start or end, include an extra empty line. + +```{r} +# no leading or trailing newline +x <- glue(" + blah + ") +unclass(x) + +# both a leading and trailing newline +y <- glue(" + + blah - leading or trailing newlines can be added explicitly - as long as the trailing line contains at least one whitespace character - ") +unclass(y) ``` -You can use `\\` at the end of a line continues to continue a single -line: +## Delimiters + +By default, code to be evaluated goes inside `{}` in a glue string. +If want a literal curly brace in your string, double it: ```{r} -glue(" - A formatted string \\ - can also be on a \\ - single line - ") +glue("The name of the package is {name}, not {{name}}.") ``` +Sometimes it's just more convenient to use different delimiters altogether, especially if the template text comes from elsewhere or is subject to external requirements. +Consider this example where we want to interpolate the function name into a code snippet that defines a function: -## Glue in pipes and pipelines +```{r} +fn_def <- " + <> <- function(x) { + # imagine a function body here + }" +glue(fn_def, NAME = "my_function", .open = "<<", .close = ">>") +``` + +In this glue string, `{` and `}` have very special meaning. +If we forced ourselves to double them, suddenly it doesn't look like normal R code anymore. +Using alternative delimiters is a nice option in cases like this. + +## Where glue looks for values + +By default, `glue()` evaluates the code inside `{}` in the caller environment: + +```{r, eval = FALSE} +glue(..., .envir = parent.frame()) +``` -`glue_data()` is useful with magrittr pipes. +So, for a top-level `glue()` call, that means the global environment. ```{r} -`%>%` <- magrittr::`%>%` -head(mtcars) %>% glue_data("{rownames(.)} has {hp} hp") +x <- "the caller environment" +glue("By default, `glue()` evaluates code in {x}.") +modern_r <- getRversion() >= "4.1.0" ``` -`glue()` is useful within dplyr pipelines. -```{r,message=FALSE} -library(dplyr) -head(iris) %>% - mutate(description = glue("This {Species} has a petal length of {Petal.Length}")) +But you can provide more narrowly scoped values by passing them to `glue()` in `name = value` form: + +```{r} +x <- "the local environment" +glue( + "`glue()` can access values from {x} or from {y}. {z}", + y = "named arguments", + z = "Woo!" +) +``` + +If the relevant data lives in a data frame (or list or environment), use `glue_data()` instead: + +```{r} +mini_mtcars <- head(cbind(model = rownames(mtcars), mtcars)) +rownames(mini_mtcars) <- NULL +glue_data(mini_mtcars, "{model} has {hp} hp.") +``` + +`glue_data()` is very natural to use with the pipe: + +```{r, eval = modern_r} +mini_mtcars |> + glue_data("{model} gets {mpg} miles per gallon.") ``` +Returning to `glue()`, recall that it defaults to evaluation in the caller environment. +This has happy implications inside a `dplyr::mutate()` pipeline. +The data-masking feature of `mutate()` means the columns of the target data frame are "in scope" for a `glue()` call: + +```r +library(dplyr) + +mini_mtcars |> + mutate(note = glue("{model} gets {mpg} miles per gallon.")) |> + select(note, cyl, disp) +#> note cyl disp +#> 1 Mazda RX4 gets 21 miles per gallon. 6 160 +#> 2 Mazda RX4 Wag gets 21 miles per gallon. 6 160 +#> 3 Datsun 710 gets 22.8 miles per gallon. 4 108 +#> 4 Hornet 4 Drive gets 21.4 miles per gallon. 6 258 +#> 5 Hornet Sportabout gets 18.7 miles per gallon. 8 360 +#> 6 Valiant gets 18.1 miles per gallon. 6 225 +``` ## 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. +glue has explicit support for constructing SQL statements. +Use backticks to quote identifiers. +Normal strings and numbers are quoted appropriately for your backend. ```{r} con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:") @@ -152,8 +219,7 @@ glue_sql(" ", .con = con) ``` -`glue_sql()` can be used in conjunction with parameterized queries using -`DBI::dbBind()` to provide protection for SQL Injection attacks +`glue_sql()` can be used in conjunction with parameterized queries using `DBI::dbBind()` to provide protection for SQL Injection attacks. ```{r} sql <- glue_sql(" @@ -167,9 +233,8 @@ 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. +`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(" @@ -183,9 +248,7 @@ glue_sql(" ", .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. +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*})", From 1a422b6c4b2b8b88f1828c2e6225c998334df956 Mon Sep 17 00:00:00 2001 From: Jenny Bryan Date: Tue, 27 Aug 2024 08:04:41 -0700 Subject: [PATCH 3/8] Was in the wrong place --- vignettes/glue.Rmd | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vignettes/glue.Rmd b/vignettes/glue.Rmd index 12d9758..b310fd0 100644 --- a/vignettes/glue.Rmd +++ b/vignettes/glue.Rmd @@ -149,7 +149,6 @@ So, for a top-level `glue()` call, that means the global environment. ```{r} x <- "the caller environment" glue("By default, `glue()` evaluates code in {x}.") -modern_r <- getRversion() >= "4.1.0" ``` But you can provide more narrowly scoped values by passing them to `glue()` in `name = value` form: @@ -173,7 +172,7 @@ glue_data(mini_mtcars, "{model} has {hp} hp.") `glue_data()` is very natural to use with the pipe: -```{r, eval = modern_r} +```{r, eval = getRversion() >= "4.1.0"} mini_mtcars |> glue_data("{model} gets {mpg} miles per gallon.") ``` From ec56c22a7ac4851085ec594d433eb82547180db2 Mon Sep 17 00:00:00 2001 From: "Jennifer (Jenny) Bryan" Date: Wed, 28 Aug 2024 11:01:19 -0700 Subject: [PATCH 4/8] Apply suggestions from code review Co-authored-by: Hadley Wickham --- README.Rmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.Rmd b/README.Rmd index 4a7c392..0b557ac 100644 --- a/README.Rmd +++ b/README.Rmd @@ -62,7 +62,7 @@ str_glue("My name is {name}.") #> My name is Wilma. ``` -You're not limited to using a bare symbol inside `{}`; it can basically be any little bit of R code: +You're not limited to using a bare symbol inside `{}`; it can be any little bit of R code: ```{r} name <- "Pebbles" @@ -101,7 +101,7 @@ These `glue_data()` examples also demonstrate that `glue()` is vectorized over t ### What you see is awfully close to what you get `glue()` lets you write code that makes it easy to predict what the final string will look like. -There is considerably less syntactical noise and mystery, compared to `paste()` and `sprintf()`, for example. +There is considerably less syntactical noise and mystery compared to `paste()` and `sprintf()`. Empty first and last lines are automatically trimmed, as is leading whitespace that is common across all lines. You don't have to choose between indenting your code properly and getting the output you actually want. @@ -122,7 +122,7 @@ if (TRUE) { glue is a relatively small and focused package, but there's more to it than the basic usage of `glue()` and `glue_data()` shown here. More recommended functions and resources: -* The "Get started" article (`vignette("glue", package = "glue")`) demonstrates more interesting features of `glue()` and `glue_data()`. +* The "Get started" article (`vignette("glue")`) demonstrates more interesting features of `glue()` and `glue_data()`. * `glue_sql()` and `glue_data_sql()` are specialized functions for producing SQL statements. * glue provides a couple of custom knitr engines that allow you to use glue syntax in chunks; learn more in `vignette("engines", package = "glue")`. From b078e9bdea9b15e092ed0e403c653285f6d64114 Mon Sep 17 00:00:00 2001 From: Jenny Bryan Date: Wed, 28 Aug 2024 11:07:41 -0700 Subject: [PATCH 5/8] Use function body example here too; rebuild README.md --- README.Rmd | 10 ++++++---- README.md | 23 +++++++++++++---------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/README.Rmd b/README.Rmd index 0b557ac..d742a17 100644 --- a/README.Rmd +++ b/README.Rmd @@ -105,18 +105,20 @@ There is considerably less syntactical noise and mystery compared to `paste()` a Empty first and last lines are automatically trimmed, as is leading whitespace that is common across all lines. You don't have to choose between indenting your code properly and getting the output you actually want. +Consider what happens when `glue()` is used inside the body of a function: ```{r} -if (TRUE) { +foo <- function() { glue(" A formatted string Can have multiple lines - with additional indention preserved - " - ) + with additional indention preserved") } +foo() ``` +The leading whitespace that is common to all 3 lines is absent from the result. + ## Learning more glue is a relatively small and focused package, but there's more to it than the basic usage of `glue()` and `glue_data()` shown here. diff --git a/README.md b/README.md index 5f17e2c..93657f3 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ str_glue("My name is {name}.") #> My name is Wilma. ``` -You’re not limited to using a bare symbol inside `{}`; it can basically -be any little bit of R code: +You’re not limited to using a bare symbol inside `{}`; it can be any +little bit of R code: ``` r name <- "Pebbles" @@ -116,35 +116,38 @@ vectorized over the data. `glue()` lets you write code that makes it easy to predict what the final string will look like. There is considerably less syntactical -noise and mystery, compared to `paste()` and `sprintf()`, for example. +noise and mystery compared to `paste()` and `sprintf()`. Empty first and last lines are automatically trimmed, as is leading whitespace that is common across all lines. You don’t have to choose between indenting your code properly and getting the output you actually -want. +want. Consider what happens when `glue()` is used inside the body of a +function: ``` r -if (TRUE) { +foo <- function() { glue(" A formatted string Can have multiple lines - with additional indention preserved - " - ) + with additional indention preserved") } +foo() #> A formatted string #> Can have multiple lines #> with additional indention preserved ``` +The leading whitespace that is common to all 3 lines is absent from the +result. + ## Learning more glue is a relatively small and focused package, but there’s more to it than the basic usage of `glue()` and `glue_data()` shown here. More recommended functions and resources: -- The “Get started” article (`vignette("glue", package = "glue")`) - demonstrates more interesting features of `glue()` and `glue_data()`. +- The “Get started” article (`vignette("glue")`) demonstrates more + interesting features of `glue()` and `glue_data()`. - `glue_sql()` and `glue_data_sql()` are specialized functions for producing SQL statements. - glue provides a couple of custom knitr engines that allow you to use From e593e1d125ee60dea0d1058b19bfbaf836e54926 Mon Sep 17 00:00:00 2001 From: "Jennifer (Jenny) Bryan" Date: Wed, 28 Aug 2024 12:02:54 -0700 Subject: [PATCH 6/8] Update vignettes/glue.Rmd Co-authored-by: Hadley Wickham --- vignettes/glue.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/glue.Rmd b/vignettes/glue.Rmd index b310fd0..7bd2513 100644 --- a/vignettes/glue.Rmd +++ b/vignettes/glue.Rmd @@ -22,7 +22,7 @@ library(glue) ## Gluing and interpolating -`glue()` can be used to glue together some pieces of text: +`glue()` can be used to glue together pieces of text: ```{r} glue("glue ", "some ", "text ", "together") From 79e932d87d1cd4ee8476b6ccbc0edce0c3f142a4 Mon Sep 17 00:00:00 2001 From: Jenny Bryan Date: Wed, 28 Aug 2024 12:54:10 -0700 Subject: [PATCH 7/8] Take the chance to talk about the glue class and printing --- vignettes/glue.Rmd | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/vignettes/glue.Rmd b/vignettes/glue.Rmd index 7bd2513..ae0fd6e 100644 --- a/vignettes/glue.Rmd +++ b/vignettes/glue.Rmd @@ -112,6 +112,26 @@ y <- glue(" unclass(y) ``` +We use `unclass()` above to make it easier to see the absence and presence of the newlines, i.e. to reveal the literal `\n` escape sequences. +`glue()` and friends generally return a glue object, which is a character vector with the S3 class `"glue"`. +The `"glue"` class exists primarily for the sake of a print method, which displays the natural formatted result of a glue string. +Most of the time this is *exactly* what the user wants to see. +The example above happens to be an exception, where we really do want to see the underlying string representation. + +Here's another example to drive home the difference between printing a glue object and looking at its string representation: + +```{r} +x <- glue(' + abc + " } + + xyz') +class(x) + +x +unclass(x) +``` + ## Delimiters By default, code to be evaluated goes inside `{}` in a glue string. From 0e9a6ac5bf96dbd403d4138f27770c4bfc119679 Mon Sep 17 00:00:00 2001 From: Jenny Bryan Date: Wed, 28 Aug 2024 12:58:19 -0700 Subject: [PATCH 8/8] Add as.character() --- vignettes/glue.Rmd | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vignettes/glue.Rmd b/vignettes/glue.Rmd index ae0fd6e..73e0758 100644 --- a/vignettes/glue.Rmd +++ b/vignettes/glue.Rmd @@ -118,7 +118,8 @@ The `"glue"` class exists primarily for the sake of a print method, which displa Most of the time this is *exactly* what the user wants to see. The example above happens to be an exception, where we really do want to see the underlying string representation. -Here's another example to drive home the difference between printing a glue object and looking at its string representation: +Here's another example to drive home the difference between printing a glue object and looking at its string representation. +`as.character()` is a another way to do this that is arguably more expressive. ```{r} x <- glue(' @@ -130,6 +131,7 @@ class(x) x unclass(x) +as.character(x) ``` ## Delimiters