knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library("dovetail")
The purpose of the dovetail package is to provide a way for Carpentries lesson contributors to write challenge and solution blocks in a simple and straightforward manner. We have taken inspiration from the Roxygen2 style of writing documentation above code blocks. For example, here is a function with documentation written above it with minimal markup:
#' Add two numbers #' #' @param x a number #' @param y a number #' @return the sum of x and y #' #' @export #' @examples #' add(1, 2) # 3 #' add(2, 1) add <- function(x, y) { x + y }
In the same vein, we could use this sort of syntax to create our own solution for generating special callout blocks within the Carpentries lessons:
```{challenge, eval = FALSE, coo = TRUE}
x <- c(5.4, 6.2, 7.1, 4.8, 7.5) names(x) <- c('a', 'b', 'c', 'd', 'e') print(x)
x_subset <- x[x<7 & x>4] print(x_subset)
## Motivation The benefit of this is method that it's legible without conversion. Our motivation for writing this stems from the way challenge blocks in lessons are constructed via block quotes. While this is fine for regular text, it can be challenging to write code examples AND ensure that they will render correctly. Consider the following block quote (taken from episode 6 of R novice gapminder) that gives a challenge block with a single solution block nested within it. Within each of these blocks is a code block with R code that should be evaluated.
Challenge 2
Given the following code:
r x <- c(5.4, 6.2, 7.1, 4.8, 7.5) names(x) <- c('a', 'b', 'c', 'd', 'e') print(x)
Write a subsetting command to return the values in x that are greater than 4 and less than 7.
Solution to challenge 2
r x_subset <- x[x<7 & x>4] print(x_subset)
{: .solution} {: .challenge}
When this gets processed by Jekyll, the kramdown tags (`{: .soltuion}`, `{: .challenge}`) are replaced by tags whose underlying css describes how the elements should be displayed. Here is what the resulting HTML would look like: ````html <blockquote class="challenge"> <h2 id="challenge-2">Challenge 2</h2> <p>Given the following code:</p> <!-- SNIP: highlighted R code and output --> <p>Write a subsetting command to return the values in x that are greater than 4 and less than 7.</p> <blockquote class="solution"> <h2 id="solution-to-challenge-2">Solution to challenge 2<span class="fold-unfold glyphicon glyphicon-collapse-down"></span></h2> <!-- SNIP: highlighted R code and output --> </blockquote> </blockquote> ```` The nesting here can be a significant distraction for contributors to lesson templates because of a few reasons: 1. if you don't have the spacing correct, Jekyll throws strange errors 2. syntax highlighting is not available within block quotes 3. evaluation of code within a block quote involves manual copying and pasting Of course, it's the two tags at the bottom that are doing *a lot* of the work in conjunction with the kramdown parser employed by Jekyll. The idea behind this package is to write an engine for knitr that will parse the above code block to look like this: ```{challenge "245:1-264:14", coo = TRUE, echo = TRUE} #' ## Challenge 2 #' #' Given the following code: #' ```r x <- c(5.4, 6.2, 7.1, 4.8, 7.5) names(x) <- c('a', 'b', 'c', 'd', 'e') print(x) #' ``` #' #' Write a subsetting command to return the values in x that are greater than 4 and less than 7. #' #' @solution Solution to challenge 2 #' #' ```r x_subset <- x[x<7 & x>4] print(x_subset) #' ```
At the moment, we are writing this to parse code chunks within RMarkdown documents, with the emphasis on R chunks. Because support for python exists via {reticulate}, it should be possible to extend this to Python, but the initial proof of concept will focus on R.
Importantly, we are not seeking to introduce novel concepts; we want to use existing terminology and tokens to make writing these blocks less complex
#'
) indicate markdown prose#' ```r
indicates the beginning of an R chunkknitr::knit_global()
)The Carpentries lessons support the following tags to open div tags within the
code block:
r paste0("#' @", dovetail:::OUR_TAGS[dovetail:::OUR_TAGS != "end"])
. Unless preceded by
#' @end
, each successive div tag will be nested within the previous tag.
These tags behave by the following rules:
#' @end
tag#' @end
tag#' @solution
)If we are going via a knitr engine route, then we need to apply our function
for handling the engine via knitr::knit_engines$set()
. What happens inside of
that engine is ultimately up to us.
The steps we take are:
knitr::engines$set()
options$code
via parse_block()
knitr::knit()
and the output of that is returned.Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.