From 0ddb1cac7118f9b3d8dcf19167be0b5b076aab6a Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Fri, 17 Nov 2023 10:55:08 -0800 Subject: [PATCH 01/16] update sandpaper fragment setup md5sum --- inst/sandpaper-fragment/site/built/md5sum.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/sandpaper-fragment/site/built/md5sum.txt b/inst/sandpaper-fragment/site/built/md5sum.txt index 6f742f47..2aae39ed 100644 --- a/inst/sandpaper-fragment/site/built/md5sum.txt +++ b/inst/sandpaper-fragment/site/built/md5sum.txt @@ -3,5 +3,5 @@ "index.md" "fe56dfea3c4269cceb361470e2ca1437" "site/built/index.md" "2022-05-25" "episodes/intro.Rmd" "3deb3673b36c3fd801e23cb85d9cd195" "site/built/intro.md" "2022-05-25" "instructors/a.md" "bbaa216f7ef3b17267c460a8bd4fda6b" "site/built/a.md" "2022-05-25" -"learners/setup.md" "0e4e4d7028efbf7d756a08217126884f" "site/built/setup.md" "2022-05-25" +"learners/setup.md" "975096ee5adaf14b68c2ef89c2cade78" "site/built/setup.md" "2023-11-17" "profiles/b.md" "bbaa216f7ef3b17267c460a8bd4fda6b" "site/built/b.md" "2022-05-25" From 14e38d6d893997710917d9b4d09f72350d14c9bb Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Fri, 17 Nov 2023 10:55:52 -0800 Subject: [PATCH 02/16] update lesson_fragment() with more documentation --- R/lesson_fragment.R | 37 +++++++++++++++++++++++++++++-------- man/lesson_fragment.Rd | 35 ++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/R/lesson_fragment.R b/R/lesson_fragment.R index 49e7f21a..0f717206 100644 --- a/R/lesson_fragment.R +++ b/R/lesson_fragment.R @@ -1,15 +1,36 @@ -#' An example lesson fragment +#' Example Lesson Fragments #' -#' This example was taken from the python novice gapminder lesson -#' @param name the name of the lesson fragment +#' Partial lessons mainly used for testing and demonstration purposes #' -#' @return a path to a lesson fragment that contains one `_episodes` directory -#' with three files: "10-lunch.md", "14-looping-data-sets.md", and -#' "17-scope.md" +#' @note +#' The lesson-fragment example was taken from the python novice gapminder +#' lesson +#' +#' @param name the name of the lesson fragment. Can be one of: +#' - lesson-fragment +#' - rmd-lesson +#' - sandpaper-fragment +#' - sandpaper-fragment with children +#' +#' @return a path to a lesson fragment whose contents are: +#' - `lesson-fragment` contains one `_episodes` directory with three files: +#' "10-lunch.md", "14-looping-data-sets.md", and "17-scope.md" +#' - `rmd-fragment` contains one episode under `_episodes_rmd` called +#' `01-test.Rmd`. +#' - `sandpaper-fragment` contains a trimmed-down Workbench lesson that +#' has its R Markdown content pre-built +#' - `sandpaper-fragment-with-children` contains much of the same content as +#' `sandpaper-fragment`, but the `episodes/index.Rmd` file references child +#' documents. #' @export #' @examples -#' #' lesson_fragment() +#' lesson_fragment("rmd-lesson") +#' lesson_fragment("sandpaper-fragment") +#' lesson_fragment("sandpaper-fragment-with-children") lesson_fragment <- function(name = "lesson-fragment") { - system.file(name, package = "pegboard") + allowed <- c("lesson-fragment", "rmd-lesson", + "sandpaper-fragment", "sandpaper-fragment-with-children") + name <- match.arg(name, allowed) + return(system.file(name, package = "pegboard")) } diff --git a/man/lesson_fragment.Rd b/man/lesson_fragment.Rd index 4c1c5ccf..cf190802 100644 --- a/man/lesson_fragment.Rd +++ b/man/lesson_fragment.Rd @@ -2,22 +2,43 @@ % Please edit documentation in R/lesson_fragment.R \name{lesson_fragment} \alias{lesson_fragment} -\title{An example lesson fragment} +\title{Example Lesson Fragments} \usage{ lesson_fragment(name = "lesson-fragment") } \arguments{ -\item{name}{the name of the lesson fragment} +\item{name}{the name of the lesson fragment. Can be one of: +\itemize{ +\item lesson-fragment +\item rmd-lesson +\item sandpaper-fragment +\item sandpaper-fragment with children +}} } \value{ -a path to a lesson fragment that contains one \verb{_episodes} directory -with three files: "10-lunch.md", "14-looping-data-sets.md", and -"17-scope.md" +a path to a lesson fragment whose contents are: +\itemize{ +\item \code{lesson-fragment} contains one \verb{_episodes} directory with three files: +"10-lunch.md", "14-looping-data-sets.md", and "17-scope.md" +\item \code{rmd-fragment} contains one episode under \verb{_episodes_rmd} called +\code{01-test.Rmd}. +\item \code{sandpaper-fragment} contains a trimmed-down Workbench lesson that +has its R Markdown content pre-built +\item \code{sandpaper-fragment-with-children} contains much of the same content as +\code{sandpaper-fragment}, but the \code{episodes/index.Rmd} file references child +documents. +} } \description{ -This example was taken from the python novice gapminder lesson +Partial lessons mainly used for testing and demonstration purposes +} +\note{ +The lesson-fragment example was taken from the python novice gapminder +lesson } \examples{ - lesson_fragment() +lesson_fragment("rmd-lesson") +lesson_fragment("sandpaper-fragment") +lesson_fragment("sandpaper-fragment-with-children") } From 8bbc257ba307bc0e5257469e22b08eda24278947 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Fri, 17 Nov 2023 11:13:00 -0800 Subject: [PATCH 03/16] fix typos --- R/lesson_fragment.R | 8 ++++---- man/lesson_fragment.Rd | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/R/lesson_fragment.R b/R/lesson_fragment.R index 0f717206..11200778 100644 --- a/R/lesson_fragment.R +++ b/R/lesson_fragment.R @@ -10,7 +10,7 @@ #' - lesson-fragment #' - rmd-lesson #' - sandpaper-fragment -#' - sandpaper-fragment with children +#' - sandpaper-fragment with child #' #' @return a path to a lesson fragment whose contents are: #' - `lesson-fragment` contains one `_episodes` directory with three files: @@ -19,7 +19,7 @@ #' `01-test.Rmd`. #' - `sandpaper-fragment` contains a trimmed-down Workbench lesson that #' has its R Markdown content pre-built -#' - `sandpaper-fragment-with-children` contains much of the same content as +#' - `sandpaper-fragment-with-child` contains much of the same content as #' `sandpaper-fragment`, but the `episodes/index.Rmd` file references child #' documents. #' @export @@ -27,10 +27,10 @@ #' lesson_fragment() #' lesson_fragment("rmd-lesson") #' lesson_fragment("sandpaper-fragment") -#' lesson_fragment("sandpaper-fragment-with-children") +#' lesson_fragment("sandpaper-fragment-with-child") lesson_fragment <- function(name = "lesson-fragment") { allowed <- c("lesson-fragment", "rmd-lesson", - "sandpaper-fragment", "sandpaper-fragment-with-children") + "sandpaper-fragment", "sandpaper-fragment-with-child") name <- match.arg(name, allowed) return(system.file(name, package = "pegboard")) } diff --git a/man/lesson_fragment.Rd b/man/lesson_fragment.Rd index cf190802..2f3bb7c5 100644 --- a/man/lesson_fragment.Rd +++ b/man/lesson_fragment.Rd @@ -12,7 +12,7 @@ lesson_fragment(name = "lesson-fragment") \item lesson-fragment \item rmd-lesson \item sandpaper-fragment -\item sandpaper-fragment with children +\item sandpaper-fragment with child }} } \value{ @@ -24,7 +24,7 @@ a path to a lesson fragment whose contents are: \code{01-test.Rmd}. \item \code{sandpaper-fragment} contains a trimmed-down Workbench lesson that has its R Markdown content pre-built -\item \code{sandpaper-fragment-with-children} contains much of the same content as +\item \code{sandpaper-fragment-with-child} contains much of the same content as \code{sandpaper-fragment}, but the \code{episodes/index.Rmd} file references child documents. } @@ -40,5 +40,5 @@ lesson lesson_fragment() lesson_fragment("rmd-lesson") lesson_fragment("sandpaper-fragment") -lesson_fragment("sandpaper-fragment-with-children") +lesson_fragment("sandpaper-fragment-with-child") } From 35d9df5e03602a4cc9272cc1d986c279e18bdbd6 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Fri, 17 Nov 2023 11:51:25 -0800 Subject: [PATCH 04/16] start lesson object vignette --- _pkgdown.yml | 3 +- man/rmd-fragments/intro-class.Rmd | 11 ++++ man/rmd-fragments/jekyll-syntax.Rmd | 43 +++++++++++++ vignettes/intro-episode.Rmd | 57 ++--------------- vignettes/intro-lesson.Rmd | 99 +++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 52 deletions(-) create mode 100644 man/rmd-fragments/intro-class.Rmd create mode 100644 man/rmd-fragments/jekyll-syntax.Rmd create mode 100644 vignettes/intro-lesson.Rmd diff --git a/_pkgdown.yml b/_pkgdown.yml index bef2660c..abbd3ae5 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -90,6 +90,7 @@ articles: - title: "Developer Guides" navbar: ~ contents: - - articles/validation - intro-xml - intro-episode + - intro-lesson + - articles/validation diff --git a/man/rmd-fragments/intro-class.Rmd b/man/rmd-fragments/intro-class.Rmd new file mode 100644 index 00000000..34a305c8 --- /dev/null +++ b/man/rmd-fragments/intro-class.Rmd @@ -0,0 +1,11 @@ +The {pegboard} package facilitates the analysis and manipulation of Markdown and +R Markdown files by translating them to XML and back again. This extends the +{tinkr} package (see `vignette("tinkr", package = "tinkr")`) by providing +additional methods that are specific for Carpentries-style lessons. There are +two `R6` classes defined in {pegboard}: + + - `Episode` objects that contain the XML data, YAML metadata and extra fields + that define the child and parent files for a particular episode. These + inherit from the [tinkr::yarn] R6 class. +- `Lesson` objects that contain lists of `Episode` objects categorised as + "episodes", "extra", or "children". diff --git a/man/rmd-fragments/jekyll-syntax.Rmd b/man/rmd-fragments/jekyll-syntax.Rmd new file mode 100644 index 00000000..46faf3d2 --- /dev/null +++ b/man/rmd-fragments/jekyll-syntax.Rmd @@ -0,0 +1,43 @@ +The former Jekyll syntax used [kramdown-flavoured +markdown](https://kramdown.gettalong.org/syntax.html), which evolved separately +from [commonmark](https://spec.commonmark.org/), the syntax that {pegboard} +knows and that Pandoc-flavoured markdown extends. One of the key differences +with the kramdown syntax is that it used something known as [Inline Attribute +Lists (IAL)](https://kramdown.gettalong.org/syntax.html#inline-attribute-lists) to +help define classes for markdown elements. These elements were formated as +`{: }` where `` is replaced by class definitions and +key/value pairs. They always appear _after_ the relevant block which lead to +code blocks that looked like this: + +````markdown +~~~ +ls -larth /path/to/dir +~~~ +{: .language-bash} +```` + +Moreover, to achieve the special callout blocks, we used blockquotes that were +given special classes (which is an accessbility no-no because those blocks were +not semantic HTML) and the nesting of these block quotes looked like this: + + +````markdown +> ## Challenge +> +> How do you list all files in a directory in reverse order by the time it was +> last updated? +> +> > ## Solution +> > +> > ~~~ +> > ls -larth /path/to/dir +> > ~~~ +> > {: .language-bash} +> {: .solution} +{: .challenge} +```` + +One of the biggest challenges with this for authors was that, unless you used an +editor like vim or emacs, this was difficult to write with all the prefixed +blockquote characters and keeping track of which IALs belonged to which block. + diff --git a/vignettes/intro-episode.Rmd b/vignettes/intro-episode.Rmd index c7e969a6..043bfb38 100644 --- a/vignettes/intro-episode.Rmd +++ b/vignettes/intro-episode.Rmd @@ -16,15 +16,8 @@ knitr::opts_chunk$set( ## Introduction -The {pegboard} package facilitates the analysis and manipulation of Markdown and -R Markdown files by translating them to XML and back again. This extends the -{tinkr} package by providing additional methods that are specific for -Carpentries-style lessons. There are two `R6` classes defined in {pegboard}: - - - `Episode` objects that contain the XML data, YAML metadata and extra fields - that define the child and parent files for a particular episode - - `Lesson` objects that contain lists of `Episode` objects categorised as - "episodes", "extra", or "children" +```{r child="../man/rmd-fragments/intro-class.Rmd"} +``` This vignette will be discussing the structure of Episode objects, how to query the contents with the {xml2} package, and how to use the methods and @@ -49,8 +42,8 @@ library("fs") ``` This is what our lesson fragment looks like. It is a fragment because it's main -purpose is to be used for examples and tests, but it contains the basic structure -of a lesson that we want. +purpose is to be used for examples and tests, but it contains the basic +structure of a lesson that we want. ```{r intro-read-noshow, echo = FALSE} dir_tree(lesson_fragment("sandpaper-fragment"), recurse = 1, regex = "site/[^R].*", invert = TRUE) @@ -317,48 +310,10 @@ written initially as a way to explore the structure of our lessons. ### The Syntax of Jekyll Lessons -The former Jekyll syntax used [kramdown-flavoured -markdown](https://kramdown.gettalong.org/syntax.html), which evolved separately -from [commonmark](https://spec.commonmark.org/), the syntax that {pegboard} -knows and that Pandoc-flavoured markdown extends. One of the key differences -with the kramdown syntax is that it used something known as [Inline Attribute -Lists (IAL)](https://kramdown.gettalong.org/syntax.html#inline-attribute-lists) to -help define classes for markdown elements. These elements were formated as -`{: }` where `` is replaced by class definitions and -key/value pairs. They always appear _after_ the relevant block which lead to -code blocks that looked like this: - -````markdown -~~~ -ls -larth /path/to/dir -~~~ -{: .language-bash} -```` - -Moreover, to achieve the special callout blocks, we used blockquotes that were -given special classes (which is an accessbility no-no because those blocks were -not semantic HTML) and the nesting of these block quotes looked like this: +```{r child="../man/rmd-fragments/jekyll-syntax.Rmd"} +``` -````markdown -> ## Challenge -> -> How do you list all files in a directory in reverse order by the time it was -> last updated? -> -> > ## Solution -> > -> > ~~~ -> > ls -larth /path/to/dir -> > ~~~ -> > {: .language-bash} -> {: .solution} -{: .challenge} -```` - -One of the biggest challenges with this for authors was that, unless you used an -editor like vim or emacs, this was difficult to write with all the prefixed -blockquote characters and keeping track of which IALs belonged to which block. ### Special methods and active bindings diff --git a/vignettes/intro-lesson.Rmd b/vignettes/intro-lesson.Rmd new file mode 100644 index 00000000..14ae0b61 --- /dev/null +++ b/vignettes/intro-lesson.Rmd @@ -0,0 +1,99 @@ +--- +title: "Introduction to the Lesson Object" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Introduction to the Lesson Object} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +## Introduction + +```{r child="../man/rmd-fragments/intro-class.Rmd"} +``` + +If you have a list of `Episode` objects, you can achieve _most_ of what you can +with the lesson objects, because much of the `Lesson` object's function is to +provide a methods that map over all of the `Episode` object methods. The key +difference between `Lesson` objects and a list of `Episode` objects is that +the `Lesson` object will collapse summaries and to map relations between +`Episodes` and their children or parent documents. + +**Before you read this vignette, please read the vignette on the `Episode` object +(`vignette("intro-episode", package = "pegboard")`)** so that you can understand +the methods that come from the `Episode` objects. In this vignette, we will talk +about the structure of `Lesson` objects, how to use the basic methods of these +objects, inspecting summaries of source vs built episodes, and assessing the +lineage of episodes that have parents and/or children documents. But first, +because of a default parameter, I need to explain a little bit about Jekyll, +the former lesson infrastructure. + +### A Brief Word About History and Jekyll + +Prior to The Workbench, we had the [styles +repository](https://github.com/carpentries/styles/), which was an all-in-one +toolbox that built websites with Jekyll. It was colloquially known as the +"Lesson Template." It has two major differences to The Workbench: folder +structure and syntax. + +#### Folder Structure + +The folder structure of lessons built with Jekyll was one where content and +tooling lived side-by-side. This folder structure looked something like this: + + +```{r, jekyll-folders, echo = FALSE} +writeLines(c( + ".", + "\U{251C}\U{2500}\U{2500} Gemfile", + "\U{251C}\U{2500}\U{2500} Makefile", + "\U{251C}\U{2500}\U{2500} _config.yml", + "\U{251C}\U{2500}\U{2500} _episodes/", + "\U{251C}\U{2500}\U{2500} _episodes_rmd/", + "\U{251C}\U{2500}\U{2500} _extras/", + "\U{251C}\U{2500}\U{2500} _includes/", + "\U{251C}\U{2500}\U{2500} _layouts/", + "\U{251C}\U{2500}\U{2500} aio.md", + "\U{251C}\U{2500}\U{2500} assets/", + "\U{251C}\U{2500}\U{2500} bin/", + "\U{251C}\U{2500}\U{2500} fig/", + "\U{251C}\U{2500}\U{2500} index.md", + "\U{251C}\U{2500}\U{2500} reference.md", + "\U{251C}\U{2500}\U{2500} requirements.txt", + "\U{2514}\U{2500}\U{2500} setup.md" +)) +``` + +When {pegboard} was first written, we initially assumed this folder structure, +where R Markdown and regular Markdown episodes lived in different folders (and +more often than not, the outputs of the R Markdown files lived inside the +`_episodes/` folder. The main method of organising episodes was by numbers +embedded in the name of the files. + +As The Workbench developed, it was clear that this folder structure needed to +change, but we needed to keep compatibility with the old lessons because we +want to ensure that people can independently convert from the old style lessons +to the new style, thus we added the `jekyll` parameter to the `Lesson` object +initializer method, and set `jekyll = TRUE` as the default to keep backwards +compatibility. + +#### Syntax + +```{r child="../man/rmd-fragments/jekyll-syntax.Rmd"} +``` + +## Creating a New Lesson Object + +The `Lesson` object is invoked with the [Lesson] `$new()`, method specifying +whether or not the lesson is derived from Jekyll. + +```{r setup} +library(pegboard) +``` From 39c93b517c2b50c26bf1ac4765d8ff9f4fddfb60 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Fri, 17 Nov 2023 15:44:26 -0800 Subject: [PATCH 05/16] add some childish stuff --- vignettes/intro-lesson.Rmd | 97 +++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/vignettes/intro-lesson.Rmd b/vignettes/intro-lesson.Rmd index 14ae0b61..a9d7dac9 100644 --- a/vignettes/intro-lesson.Rmd +++ b/vignettes/intro-lesson.Rmd @@ -91,9 +91,102 @@ compatibility. ## Creating a New Lesson Object -The `Lesson` object is invoked with the [Lesson] `$new()`, method specifying -whether or not the lesson is derived from Jekyll. +The `Lesson` object is invoked with the `Lesson$new()`, method. Here, I will +demonstrate a Workbench lesson. This is the folder structure of the workbench +lesson: + +```{r intro-read-noshow, echo = FALSE} +withr::with_dir(pegboard::lesson_fragment("sandpaper-fragment"), { + fs::dir_tree(regex = "site/[^R].*", invert = TRUE) +}) +``` + +To read it in, because we have a Workbench lesson, we need to specify `jekyll = +FALSE` to register all the div tags and ensure that the lesson is being treated +like a Workbench lesson. ```{r setup} library(pegboard) +wbfragment <- lesson_fragment("sandpaper-fragment") +print(wbfragment) # path to the lesson +wb_lesson <- Lesson$new(wbfragment, jekyll = FALSE) +print(wb_lesson) +``` + +The Lesson printing here shows that it has a subset of methods that are named +similarly to methods and active bindings from the Episode class. These are not +inherited, but rather they are implemented across all the Episode objects. The +Episode objects themselves are parsed into one of three elements: "episodes", +"children", and "extra" (NOTE: the "extra" slot may be superceded by elements +that better match the folder structure of lessons). + +```{r show-episodes} +lapply(wb_lesson$episodes, class) +lapply(wb_lesson$children, class) +lapply(wb_lesson$extra, class) +``` + +## File Information + +The Lesson object contains information about the file information: + +```{r show-files} +# what is the root path for the lesson? +wb_lesson$path +# what episode files exist? +wb_lesson$files +# do any of the files have children (Workbench lessons only)? +wb_lesson$has_children +``` + +### For the Children + +The Lesson object is very useful with Lessons that contain child documents. +Take for example the same lesson, but `episodes/intro.Rmd` has the child +`episodes/files/cat.Rmd` which in turn has the child +`episodes/files/session.Rmd`: + +```{r intro-read-noshow-children, echo = FALSE} +withr::with_dir(lesson_fragment("sandpaper-fragment-with-child"), { + fs::dir_tree(regex = "site/[^R].*", invert = TRUE) +}) +``` + +In this case, the Lesson object will detect that at least one Episode +references a child document and reads them in: + +```{r show-children-files} +wbchild <- lesson_fragment("sandpaper-fragment-with-child") +wb_lesson_child <- Lesson$new(wbchild, jekyll = FALSE) +wb_lesson_child$has_children +lapply(wb_lesson_child$children, class) ``` + +The reason it is useful is because if you have a child Episode object, you can +determine its parent and its final ancestor. + +```{r show-parent} +# cat.Rmd's parent is intro.Rmd +wb_lesson_child$children[[1]]$name +wb_lesson_child$children[[1]]$parents +wb_lesson_child$children[[1]]$build_parents + +# session.Rmd's parent is cat.Rmd +wb_lesson_child$children[[2]]$name +wb_lesson_child$children[[2]]$parents +wb_lesson_child$children[[2]]$build_parents +``` + +If you have the name of the final ancestor, then you can determine the full +lineage with the `$trace_lineage()` method, which is useful for determining if +a file should be rebuilt: + +```{r show-lineage} +parent <- wb_lesson_child$children[[2]]$build_parents +print(parent) +wb_lesson_child$trace_lineage(parent) +``` + + + + From a6553bed7f5276aec3136b84a3789df4d44c1c1a Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Mon, 20 Nov 2023 10:10:32 -0800 Subject: [PATCH 06/16] fix terminology from child file -> document --- R/find_children.R | 62 +++++++++++++++++++++++--------------------- man/find_children.Rd | 32 +++++++++++------------ man/load_children.Rd | 20 +++++++------- 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/R/find_children.R b/R/find_children.R index a2866a31..db87f803 100644 --- a/R/find_children.R +++ b/R/find_children.R @@ -1,4 +1,4 @@ -#' Detect the child files of an Episode object +#' Detect the child documents of an Episode object #' #' @description #' - `find_children()` returns the _immediate_ children for any given Episode @@ -11,7 +11,7 @@ #' @param ancestor an [Episode] object that is used to determine the parent path #' this also can be `NULL`. #' @param lsn a [Lesson] object that contains the `parent` and all its children. -#' @return a character vector of the absolute paths to child files. +#' @return a character vector of the absolute paths to child documents #' #' @details #' @@ -43,7 +43,7 @@ #' ## Tracing full lineages #' #' It is possible for a child document to have further children defined, but -#' there is a caveat: The child file is going to be read from the context of +#' there is a caveat: The child document is going to be read from the context of #' the `root.dir` knitr option, which in {sandpaper} is set to be `site/built` #' after the markdown contents and assets are copied over. #' @@ -55,25 +55,25 @@ #' ```` #' #' When an Episode is read _in the context of a Lesson_, the children are -#' processed with [load_children()] so that each file with children will have a +#' processed with [load_children()] so that each document with children will have a #' non-zero value of the `$children` element. We recurse through the `$children` -#' element in the [Lesson] object to exhaust the search for the children files. +#' element in the [Lesson] object to exhaust the search for the child documents. #' #' The `trace_children()` will return the entire lineage for a given _parent_ -#' file. Which, in the case of the examples defined above would be: +#' document. Which, in the case of the examples defined above would be: #' `/path/to/episodes/parent.Rmd`, `/path/to/episodes/files/the-child.Rmd`, #' and `/path/to/episodes/files/the-grandchild.md`. #' #' ## NOTE #' -#' For standard lessons, child files are written relative to the directory of -#' the build parent file. Usually, these child files will be in the `files` -#' folder under their parent folder. Overview lessons are a little different. -#' For overview lessons (in The Workbench, these are lessons which contain -#' `overview: true` in config.yaml), the child files may point to -#' `files/child.md`, but in reality, the child file is at the root of the +#' For standard lessons, child documents are written relative to the directory +#' of the build parent document. Usually, these child documents will be in the +#' `files` folder under their parent folder. Overview lessons are a little +#' different. For overview lessons (in The Workbench, these are lessons which +#' contain `overview: true` in config.yaml), the child documents may point to +#' `files/child.md`, but in reality, the child document is at the root of the #' lesson `../files/child.md`. We correct for this by first checking that the -#' child files exist and if they don't defaulting to the top of the lesson. +#' child documents exist and if they don't defaulting to the top of the lesson. #' #' @keywords internal #' @rdname find_children @@ -88,15 +88,15 @@ #' # `find_children()` -------------------------------------------------------- #' ex <- lesson_fragment("sandpaper-fragment-with-child") #' -#' # The introduction has a single child file +#' # The introduction has a single child document #' intro <- tinkr::yarn$new(fs::path(ex, "episodes", "intro.Rmd")) -#' intro$head(21) # show the child file +#' intro$head(21) # show the child document #' pb$find_children(intro) #' # this is identical to the `$children` element of an Episode object #' ep <- Episode$new(fs::path(ex, "episodes", "intro.Rmd")) #' ep$children #' -#' # Loading the child file reveals another child +#' # Loading the child document reveals another child #' child <- Episode$new(ep$children[[1]]) #' child$children #' child$show() @@ -116,7 +116,7 @@ #' ) find_children <- function(parent, ancestor = NULL) { code_blocks <- get_code(parent$body, type = NULL, attr = NULL) - children <- child_file_from_code_blocks(code_blocks) + children <- child_document_from_code_blocks(code_blocks) any_children <- length(children) > 0L ancestor_has_parents <- identical(ancestor$has_parents, TRUE) parent_has_parents <- identical(parent$has_parents, TRUE) @@ -160,7 +160,7 @@ find_children <- function(parent, ancestor = NULL) { return(children) } # get the child file from code block if it exists -child_file_from_code_blocks <- function(nodes) { +child_document_from_code_blocks <- function(nodes) { use_children <- xml2::xml_has_attr(nodes, "child") if (any(use_children)) { nodes <- nodes[use_children] @@ -183,8 +183,8 @@ child_file_from_code_blocks <- function(nodes) { } } -# trace the lineage of a source file and return a recursive list of children -# files. This assumes that the lesson has been set up to process children +# trace the lineage of a source file and return a recursive list of child +# documents. This assumes that the lesson has been set up to process children #' @rdname find_children trace_children <- function(parent, lsn) { if (parent$has_children) { @@ -216,11 +216,13 @@ trace_children <- function(parent, lsn) { #' @details #' #' When we want to build lessons, it's important to be able to find all of the -#' files that are necessary to build a particular file. If there is a modification in a child file, \pkg{sandpaper} needs to know that it should flag the parent -#' for rebuilding. To do this, we need two pieces of information: +#' documents that are necessary to build a particular file. If there is a +#' modification in a child document, \pkg{sandpaper} needs to know that it +#' should flag the parent for rebuilding. To do this, we need two pieces of +#' information: #' -#' 1. The earliest ancestors of a given child file -#' 2. The full list of descendants of a given parent file +#' 1. The earliest ancestors of a given child document. +#' 2. The full list of descendants of a given parent document. #' #' Each Episode object only knows about itself, so it can only report its #' immediate children, but not the children of children, or even its parent @@ -228,12 +230,12 @@ trace_children <- function(parent, lsn) { #' contains the context of all of the Episodes and can provide this information. #' #' During Lesson object initialisation, the `load_children()` function is -#' called to process all source files for their children. This creates an +#' called to process all source documents for their children. This creates an #' empty list of children that is continuously appended to during the function -#' call. It then calls `read_children()` on each parent file, which will append -#' itself as a parent to any existing children in the `all_children` list, -#' intitialize new [Episode] objects from the unread child files, and then -#' search those for children until there are no children left to read. +#' call. It then calls `read_children()` on each parent document, which will +#' append itself as a parent to any existing children in the `all_children` +#' list, intitialize new [Episode] objects from the unread child documents, and +#' then search those for children until there are no children left to read. #' #' @keywords internal #' @seealso [find_children()] for details on how child documents are discovered @@ -273,7 +275,7 @@ load_children <- function(all_parents) { return(the_children) } -# Read in and/or update all recursive child files for a given parent +# Read in and/or update all recursive child documents for a given parent #' @rdname load_children read_children <- function(parent, all_children = list(), ...) { # if the parent has no children, return NULL. This is the exit condition diff --git a/man/find_children.Rd b/man/find_children.Rd index 21de5e06..8f7b4d2e 100644 --- a/man/find_children.Rd +++ b/man/find_children.Rd @@ -3,7 +3,7 @@ \name{find_children} \alias{find_children} \alias{trace_children} -\title{Detect the child files of an Episode object} +\title{Detect the child documents of an Episode object} \usage{ find_children(parent, ancestor = NULL) @@ -19,7 +19,7 @@ this also can be \code{NULL}.} \item{lsn}{a \link{Lesson} object that contains the \code{parent} and all its children.} } \value{ -a character vector of the absolute paths to child files. +a character vector of the absolute paths to child documents } \description{ \itemize{ @@ -55,7 +55,7 @@ known children for further processing. \subsection{Tracing full lineages}{ It is possible for a child document to have further children defined, but -there is a caveat: The child file is going to be read from the context of +there is a caveat: The child document is going to be read from the context of the \code{root.dir} knitr option, which in {sandpaper} is set to be \code{site/built} after the markdown contents and assets are copied over. @@ -66,26 +66,26 @@ after the markdown contents and assets are copied over. }\if{html}{\out{}} When an Episode is read \emph{in the context of a Lesson}, the children are -processed with \code{\link[=load_children]{load_children()}} so that each file with children will have a +processed with \code{\link[=load_children]{load_children()}} so that each document with children will have a non-zero value of the \verb{$children} element. We recurse through the \verb{$children} -element in the \link{Lesson} object to exhaust the search for the children files. +element in the \link{Lesson} object to exhaust the search for the child documents. The \code{trace_children()} will return the entire lineage for a given \emph{parent} -file. Which, in the case of the examples defined above would be: +document. Which, in the case of the examples defined above would be: \verb{/path/to/episodes/parent.Rmd}, \verb{/path/to/episodes/files/the-child.Rmd}, and \verb{/path/to/episodes/files/the-grandchild.md}. } \subsection{NOTE}{ -For standard lessons, child files are written relative to the directory of -the build parent file. Usually, these child files will be in the \code{files} -folder under their parent folder. Overview lessons are a little different. -For overview lessons (in The Workbench, these are lessons which contain -\code{overview: true} in config.yaml), the child files may point to -\code{files/child.md}, but in reality, the child file is at the root of the +For standard lessons, child documents are written relative to the directory +of the build parent document. Usually, these child documents will be in the +\code{files} folder under their parent folder. Overview lessons are a little +different. For overview lessons (in The Workbench, these are lessons which +contain \code{overview: true} in config.yaml), the child documents may point to +\code{files/child.md}, but in reality, the child document is at the root of the lesson \code{../files/child.md}. We correct for this by first checking that the -child files exist and if they don't defaulting to the top of the lesson. +child documents exist and if they don't defaulting to the top of the lesson. } } \examples{ @@ -99,15 +99,15 @@ pb <- asNamespace("pegboard") # `find_children()` -------------------------------------------------------- ex <- lesson_fragment("sandpaper-fragment-with-child") -# The introduction has a single child file +# The introduction has a single child document intro <- tinkr::yarn$new(fs::path(ex, "episodes", "intro.Rmd")) -intro$head(21) # show the child file +intro$head(21) # show the child document pb$find_children(intro) # this is identical to the `$children` element of an Episode object ep <- Episode$new(fs::path(ex, "episodes", "intro.Rmd")) ep$children -# Loading the child file reveals another child +# Loading the child document reveals another child child <- Episode$new(ep$children[[1]]) child$children child$show() diff --git a/man/load_children.Rd b/man/load_children.Rd index 1cdc6103..4b08dffb 100644 --- a/man/load_children.Rd +++ b/man/load_children.Rd @@ -36,11 +36,13 @@ documents } \details{ When we want to build lessons, it's important to be able to find all of the -files that are necessary to build a particular file. If there is a modification in a child file, \pkg{sandpaper} needs to know that it should flag the parent -for rebuilding. To do this, we need two pieces of information: +documents that are necessary to build a particular file. If there is a +modification in a child document, \pkg{sandpaper} needs to know that it +should flag the parent for rebuilding. To do this, we need two pieces of +information: \enumerate{ -\item The earliest ancestors of a given child file -\item The full list of descendants of a given parent file +\item The earliest ancestors of a given child document. +\item The full list of descendants of a given parent document. } Each Episode object only knows about itself, so it can only report its @@ -49,12 +51,12 @@ immediate children, but not the children of children, or even its parent contains the context of all of the Episodes and can provide this information. During Lesson object initialisation, the \code{load_children()} function is -called to process all source files for their children. This creates an +called to process all source documents for their children. This creates an empty list of children that is continuously appended to during the function -call. It then calls \code{read_children()} on each parent file, which will append -itself as a parent to any existing children in the \code{all_children} list, -intitialize new \link{Episode} objects from the unread child files, and then -search those for children until there are no children left to read. +call. It then calls \code{read_children()} on each parent document, which will +append itself as a parent to any existing children in the \code{all_children} +list, intitialize new \link{Episode} objects from the unread child documents, and +then search those for children until there are no children left to read. } \examples{ # needed for using internal functions: loading the namespace From 35da8c22cfa572bbc646d49213696e0d263a6ee5 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Mon, 20 Nov 2023 10:10:55 -0800 Subject: [PATCH 07/16] more fixes to lesson vignette; proper linking --- man/rmd-fragments/intro-class.Rmd | 10 ++-- vignettes/intro-lesson.Rmd | 88 +++++++++++++++++++------------ 2 files changed, 59 insertions(+), 39 deletions(-) diff --git a/man/rmd-fragments/intro-class.Rmd b/man/rmd-fragments/intro-class.Rmd index 34a305c8..b425d8fc 100644 --- a/man/rmd-fragments/intro-class.Rmd +++ b/man/rmd-fragments/intro-class.Rmd @@ -4,8 +4,8 @@ R Markdown files by translating them to XML and back again. This extends the additional methods that are specific for Carpentries-style lessons. There are two `R6` classes defined in {pegboard}: - - `Episode` objects that contain the XML data, YAML metadata and extra fields - that define the child and parent files for a particular episode. These - inherit from the [tinkr::yarn] R6 class. -- `Lesson` objects that contain lists of `Episode` objects categorised as - "episodes", "extra", or "children". + - `pegboard::Episode` objects that contain the XML data, YAML metadata and + extra fields that define the child and parent files for a particular + episode. These inherit from the `tinkr::yarn` R6 class. +- `pegboard::Lesson` objects that contain lists of `Episode` objects + categorised as "episodes", "extra", or "children". diff --git a/vignettes/intro-lesson.Rmd b/vignettes/intro-lesson.Rmd index a9d7dac9..57999b97 100644 --- a/vignettes/intro-lesson.Rmd +++ b/vignettes/intro-lesson.Rmd @@ -49,26 +49,28 @@ The folder structure of lessons built with Jekyll was one where content and tooling lived side-by-side. This folder structure looked something like this: -```{r, jekyll-folders, echo = FALSE} -writeLines(c( - ".", - "\U{251C}\U{2500}\U{2500} Gemfile", - "\U{251C}\U{2500}\U{2500} Makefile", - "\U{251C}\U{2500}\U{2500} _config.yml", - "\U{251C}\U{2500}\U{2500} _episodes/", - "\U{251C}\U{2500}\U{2500} _episodes_rmd/", - "\U{251C}\U{2500}\U{2500} _extras/", - "\U{251C}\U{2500}\U{2500} _includes/", - "\U{251C}\U{2500}\U{2500} _layouts/", - "\U{251C}\U{2500}\U{2500} aio.md", - "\U{251C}\U{2500}\U{2500} assets/", - "\U{251C}\U{2500}\U{2500} bin/", - "\U{251C}\U{2500}\U{2500} fig/", - "\U{251C}\U{2500}\U{2500} index.md", - "\U{251C}\U{2500}\U{2500} reference.md", - "\U{251C}\U{2500}\U{2500} requirements.txt", - "\U{2514}\U{2500}\U{2500} setup.md" -)) +```{r, jekyll-folders, echo = FALSE, comment = "# "} +writeLines(". +├── Gemfile +├── Makefile +├── _config.yml +├── \033[01;34m_episodes/\033[0m +│ └── 01-intro.md +├── \033[01;34m_episodes_rmd/\033[0m +├── \033[01;34m_extras/\033[0m +│ ├── a.md +│ └── b.md +├── \033[01;34m_includes/\033[0m +├── \033[01;34m_layouts/\033[0m +├── aio.md +├── \033[01;34massets/\033[0m +├── \033[01;34mbin/\033[0m +├── \033[01;34mfig/\033[0m +├── index.md +├── reference.md +├── requirements.txt +└── setup.md +") ``` When {pegboard} was first written, we initially assumed this folder structure, @@ -95,7 +97,7 @@ The `Lesson` object is invoked with the `Lesson$new()`, method. Here, I will demonstrate a Workbench lesson. This is the folder structure of the workbench lesson: -```{r intro-read-noshow, echo = FALSE} +```{r intro-read-noshow, echo = FALSE, comment = "# "} withr::with_dir(pegboard::lesson_fragment("sandpaper-fragment"), { fs::dir_tree(regex = "site/[^R].*", invert = TRUE) }) @@ -106,7 +108,9 @@ FALSE` to register all the div tags and ensure that the lesson is being treated like a Workbench lesson. ```{r setup} -library(pegboard) +library("pegboard") +library("glue") +library("fs") wbfragment <- lesson_fragment("sandpaper-fragment") print(wbfragment) # path to the lesson wb_lesson <- Lesson$new(wbfragment, jekyll = FALSE) @@ -139,20 +143,21 @@ wb_lesson$files wb_lesson$has_children ``` -### For the Children +## Creating a New Lesson with Child Documents -The Lesson object is very useful with Lessons that contain child documents. +The Lesson object is very useful with Lessons that contain child documents +(see the `pegboard::find_children()` documentation for details). Take for example the same lesson, but `episodes/intro.Rmd` has the child `episodes/files/cat.Rmd` which in turn has the child `episodes/files/session.Rmd`: -```{r intro-read-noshow-children, echo = FALSE} +```{r intro-read-noshow-children, echo = FALSE, comment = "# "} withr::with_dir(lesson_fragment("sandpaper-fragment-with-child"), { fs::dir_tree(regex = "site/[^R].*", invert = TRUE) }) ``` -In this case, the Lesson object will detect that at least one Episode +In this case, the `Lesson` object will detect that at least one `Episode` references a child document and reads them in: ```{r show-children-files} @@ -163,18 +168,26 @@ lapply(wb_lesson_child$children, class) ``` The reason it is useful is because if you have a child Episode object, you can -determine its parent and its final ancestor. +determine its parent and its final ancestor. Because these paths are absolute +paths, I am going to write a function that will use the {glue} package to print +it nicely for us: ```{r show-parent} +show_child_parents <- function(child) { + parents <- fs::path_rel(child$parents, start = child$lesson) + build_parents <- fs::path_rel(child$build_parents, start = child$lesson) + + msg <- "Ancestors for {child$name} --- + Parent(s): {parents} + Final ancestor(s): {build_parents}" + glue::glue(msg) +} + # cat.Rmd's parent is intro.Rmd -wb_lesson_child$children[[1]]$name -wb_lesson_child$children[[1]]$parents -wb_lesson_child$children[[1]]$build_parents +show_child_parents(wb_lesson_child$children[[1]]) # session.Rmd's parent is cat.Rmd -wb_lesson_child$children[[2]]$name -wb_lesson_child$children[[2]]$parents -wb_lesson_child$children[[2]]$build_parents +show_child_parents(wb_lesson_child$children[[2]]) ``` If you have the name of the final ancestor, then you can determine the full @@ -184,7 +197,14 @@ a file should be rebuilt: ```{r show-lineage} parent <- wb_lesson_child$children[[2]]$build_parents print(parent) -wb_lesson_child$trace_lineage(parent) +lineage <- wb_lesson_child$trace_lineage(parent) + +# printing the lineage in a presentable fashion: +rel <- wb_lesson_child$path +pretty_lineage <- path_rel(lineage, start = rel) +pretty_lineage <- glue_collapse(pretty_lineage, sep = ", ", last = ", and ") +glue("The lineage of {path_rel(parent, start = rel)} is: + {pretty_lineage}") ``` From e7279ca50b79c443f2b1d275a61f2cea1cd41544 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Mon, 20 Nov 2023 11:58:14 -0800 Subject: [PATCH 08/16] add more info about other methods --- vignettes/intro-lesson.Rmd | 120 ++++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 14 deletions(-) diff --git a/vignettes/intro-lesson.Rmd b/vignettes/intro-lesson.Rmd index 57999b97..7ff0b443 100644 --- a/vignettes/intro-lesson.Rmd +++ b/vignettes/intro-lesson.Rmd @@ -26,14 +26,14 @@ difference between `Lesson` objects and a list of `Episode` objects is that the `Lesson` object will collapse summaries and to map relations between `Episodes` and their children or parent documents. -**Before you read this vignette, please read the vignette on the `Episode` object -(`vignette("intro-episode", package = "pegboard")`)** so that you can understand -the methods that come from the `Episode` objects. In this vignette, we will talk -about the structure of `Lesson` objects, how to use the basic methods of these -objects, inspecting summaries of source vs built episodes, and assessing the -lineage of episodes that have parents and/or children documents. But first, -because of a default parameter, I need to explain a little bit about Jekyll, -the former lesson infrastructure. +**Before you read this vignette, please read the vignette on the `Episode` +object (`vignette("intro-episode", package = "pegboard")`)** so that you can +understand the methods that come from the `Episode` objects. In this vignette, +we will talk about the structure of `Lesson` objects, how to use the basic +methods of these objects, inspecting summaries of source vs built episodes, and +assessing the lineage of episodes that have parents and/or children documents. +But first, because of a default parameter, I need to explain a little bit about +Jekyll, the former lesson infrastructure. ### A Brief Word About History and Jekyll @@ -110,6 +110,7 @@ like a Workbench lesson. ```{r setup} library("pegboard") library("glue") +library("yaml") library("fs") wbfragment <- lesson_fragment("sandpaper-fragment") print(wbfragment) # path to the lesson @@ -130,6 +131,18 @@ lapply(wb_lesson$children, class) lapply(wb_lesson$extra, class) ``` +Notice here that there is only one episode in the `$episodes` item, but in the +directory tree above, we see two. This is because of the `config.yaml` file, +which defines the order of the episodes: + +```{r config-order} +read_yaml(path(wbfragment, "config.yaml"))$episodes +``` + +Because `episodes/nope.Rmd` is not listed, it is not read in. This is useful +to avoid reading in content from files that are incomplete or not correctly +formatted. + ## File Information The Lesson object contains information about the file information: @@ -143,10 +156,71 @@ wb_lesson$files wb_lesson$has_children ``` +## Basic Summaries + +As mentioned earlier, many of the methods in a `Lesson` object are wrappers +for methods in `Episode` objects. `challenges`, `solutions`, `summary`, and +the `validate_*()` methods are the obvious ones: + +```{r challenge-solution} +wb_lesson$challenges() +wb_lesson$solutions() +``` + +For summaries, you will get a data frame of the summaries. You can also choose +to include other collections in the summary: + +```{r summary} +wb_lesson$summary() # defaults to episodes +wb_lesson$summary(collection = c("episodes", "extra")) +``` + +Validation will auto-check everything: + +```{r validate} +wb_lesson$validate_divs() +wb_lesson$validate_headings() +wb_lesson$validate_links() +``` + +## Loading Built Documents + +One thing that is very useful is to check the status of the built documents +to ensure that everything you expect is there. You can load all of the built markdown documents with the `$load_built()` method and the built documents will +populate the `$built` field: + +```{r load-built} +wb_lesson$load_built() +lapply(wb_lesson$built, class) +``` + +You can use these to inspect how the content is rendered and see that the +code blocks render what they should render: + +```{r summary-built} +to_check <- c("page", "code", "output", "images", "warning", "error") +wb_lesson$summary(collection = c("episodes", "built"))[to_check] +``` + ## Creating a New Lesson with Child Documents -The Lesson object is very useful with Lessons that contain child documents -(see the `pegboard::find_children()` documentation for details). +If you are unfamiliar with the concept of child documents, please read +the "Including Child Documents" vignette in the {sandpaper} package +(`vignette("include-child-documents", package = "sandpaper")`). + +The `pegboard::Lesson` object is very useful with lessons that contain child +documents because it records the relationships between documents. This is key +for workflows determining build order of a Lesson. If a source document is +modified, in any build system, that source document will trigger a rebuild of +the downstream page, and _the same should happen if a child document of that +source is modified_ (if you are interested in the build process used by +{sandpaper}, you can read `sandpaper::build_status()` and +`sandpaper::hash_children()`). This functionality is implemented in the +`pegboard::Lesson$trace_lineage()` method, which returns all documents required +to build any given file. We will demonstrate the utility of this later, but +first, we will demonstrate how `pegboard::Lesson$new()` auto-detects the child +documents: + Take for example the same lesson, but `episodes/intro.Rmd` has the child `episodes/files/cat.Rmd` which in turn has the child `episodes/files/session.Rmd`: @@ -157,8 +231,21 @@ withr::with_dir(lesson_fragment("sandpaper-fragment-with-child"), { }) ``` -In this case, the `Lesson` object will detect that at least one `Episode` -references a child document and reads them in: +A valid child document reference requires a code chunk with a `child` attribute +that points to a valid file relative to the parent document, so if I have this +code block inside `episodes/intro.Rmd`, then it will include the child document +called `episodes/files/cat.Rmd`: + +````{verbatim} + +```{r cat-child, child="files/cat.Rmd"} +``` +```` + +During initialisation of a Workbench lesson (note: not currently implemented for +Jekyll lessons), the `Lesson` object will detect that at least one `Episode` +references at least one child document (via `find_children()`) and read them in +(see `load_children()` for details). ```{r show-children-files} wbchild <- lesson_fragment("sandpaper-fragment-with-child") @@ -207,6 +294,11 @@ glue("The lineage of {path_rel(parent, start = rel)} is: {pretty_lineage}") ``` +## Jekyll Lessons - - +There are some methods that are specific to lessons that are built with Jekyll. +In particular, the `n_problems` and `show_problems` active binding are useful +for determining if anything went wrong with parsing the kramdown syntax, +the `$isolate_blocks()` method was used to strip out all non-blockquote content, +the `$blocks()` method returned all block quote with filters for types, and +the `$rmd` field was an indicator that the lesson used R Markdown. From d3dcb2f481c58a009e627b7e47c9cb39995d3692 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Mon, 20 Nov 2023 15:58:14 -0800 Subject: [PATCH 09/16] update handouts --- vignettes/intro-episode.Rmd | 31 +++++++++++++++++++++++++++++++ vignettes/intro-lesson.Rmd | 11 +++++++++++ 2 files changed, 42 insertions(+) diff --git a/vignettes/intro-episode.Rmd b/vignettes/intro-episode.Rmd index 043bfb38..a546eb37 100644 --- a/vignettes/intro-episode.Rmd +++ b/vignettes/intro-episode.Rmd @@ -288,6 +288,37 @@ We can see that the chunks now have names, but the proof is in the rendering: intro$show() ``` +## Handouts + +**NOTE: This will change in version 0.8.0**. It is possible to generate a code +handout by using the `$handout()` method. This will grab all challenge blocks +along with any code block that contains `purl = TRUE` and strip out everything +else. You can also specifiy `solution = TRUE` to include the solutions: + +```{r handout-basic} +writeLines(intro$handout()) +writeLines(intro$handout(solution = TRUE)) +``` + +I want to show that the `purl = TRUE` method actually works, so I'll take the +`chunks` from above and include the last one: + +```{r handout-purl} +xml2::xml_set_attr(chunks[chunk_names == "pyramid"], "purl", TRUE) +writeLines(intro$handout()) +``` + +The `path` arguments allows this to be written to a file so that it can be +converted to a script using `knitr::purl` + +```{r handout-file} +tmp <- tempfile() +intro$handout(path = tmp) +writeLines(readLines(tmp)) +``` + +## Reset + One of the things about manipulating these documents in code is that it is possible to go back and reset if things are not correct, which is why we have the `$reset()` method: diff --git a/vignettes/intro-lesson.Rmd b/vignettes/intro-lesson.Rmd index 7ff0b443..ea08c642 100644 --- a/vignettes/intro-lesson.Rmd +++ b/vignettes/intro-lesson.Rmd @@ -202,6 +202,16 @@ to_check <- c("page", "code", "output", "images", "warning", "error") wb_lesson$summary(collection = c("episodes", "built"))[to_check] ``` +## Handouts + +This is another method wrapped from the Episode method, where it combines the +output into a single file and prepends the Episode title before each section: + +```{r handouts} +writeLines(wb_lesson$handout()) +``` + + ## Creating a New Lesson with Child Documents If you are unfamiliar with the concept of child documents, please read @@ -294,6 +304,7 @@ glue("The lineage of {path_rel(parent, start = rel)} is: {pretty_lineage}") ``` + ## Jekyll Lessons There are some methods that are specific to lessons that are built with Jekyll. From cec2e0a2e01304e1ba779bb94647e3a9818f7275 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Tue, 21 Nov 2023 07:55:16 -0800 Subject: [PATCH 10/16] more documentation updates --- R/Episode.R | 16 ++++++++++++++-- R/Lesson.R | 13 ++++++++++--- man/Episode.Rd | 8 ++++++-- man/Lesson.Rd | 7 ++++--- vignettes/intro-lesson.Rmd | 3 ++- 5 files changed, 36 insertions(+), 11 deletions(-) diff --git a/R/Episode.R b/R/Episode.R index 689910a6..7ad5d621 100644 --- a/R/Episode.R +++ b/R/Episode.R @@ -2,9 +2,16 @@ #' #' @description #' Wrapper around an xml document to manipulate and inspect Carpentries episodes +#' #' @details -#' This class is a fancy wrapper around the results of [tinkr::to_xml()] and -#' has method specific to the Carpentries episodes. +#' The Episode class is a superclass of [tinkr::yarn()], which transforms +#' (commonmark-formatted) Markdown to XML and back again. The extension that +#' the Episode class provides is support for both [Pandoc](https://pandoc.org) +#' and [kramdown](https://kramdown.gettalong.org/) flavours of Markdown. +#' +#' Read more about this class in `vignette("intro-episode", package = +#' "pegboard)`. +#' #' @export Episode <- R6::R6Class("Episode", inherit = tinkr::yarn, @@ -99,7 +106,12 @@ Episode <- R6::R6Class("Episode", self$yaml <- ep$yaml self$body <- ep$body self$ns <- ep$ns + # if the parents are missing, this walk will do nothing purrr::walk(parents, function(parent) add_parent(self, parent)) + # the parent here is used to determine the build path for the + # child document, which is dependent on the build parent, aka the final + # ancestor. If there is no parent, then the children are relative to the + # parent. self$children <- find_children(ep, ancestor = parents[[1]]) }, diff --git a/R/Lesson.R b/R/Lesson.R index 2fbd220e..9d5c5d6a 100644 --- a/R/Lesson.R +++ b/R/Lesson.R @@ -2,10 +2,17 @@ #' #' @description #' This is a wrapper for several [Episode] class objects. +#' #' @details -#' Lessons are made of up several episodes within the `_episodes/` directory of -#' a lesson. This class keeps track of several episodes and allows us to switch -#' between RMarkdown and markdown episodes +#' +#' This class contains and keeps track of relationships between [Episode] +#' objects contained within [Carpentries +#' Workbench](https://carpentries.github.io/workbench) and [Carpentries +#' styles](https://carpentries.github.io/lesson-example) lessons. +#' +#' Read more about how to use this class in `vignette("intro-lesson", package = +#' "pegboard")` +#' #' @export Lesson <- R6::R6Class("Lesson", public = list( diff --git a/man/Episode.Rd b/man/Episode.Rd index 18394714..79340fca 100644 --- a/man/Episode.Rd +++ b/man/Episode.Rd @@ -7,8 +7,12 @@ Wrapper around an xml document to manipulate and inspect Carpentries episodes } \details{ -This class is a fancy wrapper around the results of \code{\link[tinkr:to_xml]{tinkr::to_xml()}} and -has method specific to the Carpentries episodes. +The Episode class is a superclass of \code{\link[tinkr:yarn]{tinkr::yarn()}}, which transforms +(commonmark-formatted) Markdown to XML and back again. The extension that +the Episode class provides is support for both \href{https://pandoc.org}{Pandoc} +and \href{https://kramdown.gettalong.org/}{kramdown} flavours of Markdown. + +Read more about this class in \verb{vignette("intro-episode", package = "pegboard)}. } \note{ The current XLST spec for {tinkr} does not support kramdown, which diff --git a/man/Lesson.Rd b/man/Lesson.Rd index 29f422fe..396fbbbd 100644 --- a/man/Lesson.Rd +++ b/man/Lesson.Rd @@ -7,9 +7,10 @@ This is a wrapper for several \link{Episode} class objects. } \details{ -Lessons are made of up several episodes within the \verb{_episodes/} directory of -a lesson. This class keeps track of several episodes and allows us to switch -between RMarkdown and markdown episodes +This class contains and keeps track of relationships between \link{Episode} +objects contained within \href{https://carpentries.github.io/workbench}{Carpentries Workbench} and \href{https://carpentries.github.io/lesson-example}{Carpentries styles} lessons. + +Read more about how to use this class in \code{vignette("intro-lesson", package = "pegboard")} } \examples{ diff --git a/vignettes/intro-lesson.Rmd b/vignettes/intro-lesson.Rmd index ea08c642..b50b5305 100644 --- a/vignettes/intro-lesson.Rmd +++ b/vignettes/intro-lesson.Rmd @@ -195,7 +195,8 @@ lapply(wb_lesson$built, class) ``` You can use these to inspect how the content is rendered and see that the -code blocks render what they should render: +code blocks render what they should render. In thise case, `episodes/intro.Rmd` +will render one output block and one image. ```{r summary-built} to_check <- c("page", "code", "output", "images", "warning", "error") From 15eb9855d0f4ed3cf286ca93a1e6e64679d09565 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Tue, 21 Nov 2023 11:22:01 -0800 Subject: [PATCH 11/16] fix typo --- R/Episode.R | 2 +- man/Episode.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/Episode.R b/R/Episode.R index 7ad5d621..ffbe5f24 100644 --- a/R/Episode.R +++ b/R/Episode.R @@ -10,7 +10,7 @@ #' and [kramdown](https://kramdown.gettalong.org/) flavours of Markdown. #' #' Read more about this class in `vignette("intro-episode", package = -#' "pegboard)`. +#' "pegboard")`. #' #' @export Episode <- R6::R6Class("Episode", diff --git a/man/Episode.Rd b/man/Episode.Rd index 79340fca..4e0d8942 100644 --- a/man/Episode.Rd +++ b/man/Episode.Rd @@ -12,7 +12,7 @@ The Episode class is a superclass of \code{\link[tinkr:yarn]{tinkr::yarn()}}, wh the Episode class provides is support for both \href{https://pandoc.org}{Pandoc} and \href{https://kramdown.gettalong.org/}{kramdown} flavours of Markdown. -Read more about this class in \verb{vignette("intro-episode", package = "pegboard)}. +Read more about this class in \code{vignette("intro-episode", package = "pegboard")}. } \note{ The current XLST spec for {tinkr} does not support kramdown, which From 780712ad8026b7e99f804655b07a6908262dbe9d Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Tue, 21 Nov 2023 11:22:12 -0800 Subject: [PATCH 12/16] add more content about accessors --- vignettes/intro-lesson.Rmd | 77 ++++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/vignettes/intro-lesson.Rmd b/vignettes/intro-lesson.Rmd index b50b5305..1e0717dc 100644 --- a/vignettes/intro-lesson.Rmd +++ b/vignettes/intro-lesson.Rmd @@ -111,6 +111,7 @@ like a Workbench lesson. library("pegboard") library("glue") library("yaml") +library("xml2") library("fs") wbfragment <- lesson_fragment("sandpaper-fragment") print(wbfragment) # path to the lesson @@ -156,17 +157,34 @@ wb_lesson$files wb_lesson$has_children ``` -## Basic Summaries +## Accessors As mentioned earlier, many of the methods in a `Lesson` object are wrappers -for methods in `Episode` objects. `challenges`, `solutions`, `summary`, and -the `validate_*()` methods are the obvious ones: +for methods in `Episode` objects. `challenges`, `solutions` are the obvious +ones. ```{r challenge-solution} wb_lesson$challenges() wb_lesson$solutions() ``` +For the rest of the elements (or active bindings), you will need to use the +`$get()` method. For example, if you wanted all code blocks from the episodes +and the extra content, you would use: + +```{r get-code} +wb_lesson$get("code", collection = c("episodes", "extra")) +``` + +Similarly, for links and headings you would use: + +```{r get-links-headings} +wb_lesson$get("links", collection = c("episodes", "extra")) +wb_lesson$get("headings", collection = c("episodes", "extra")) +``` + +## Methods Summaries and Validation + For summaries, you will get a data frame of the summaries. You can also choose to include other collections in the summary: @@ -175,12 +193,18 @@ wb_lesson$summary() # defaults to episodes wb_lesson$summary(collection = c("episodes", "extra")) ``` -Validation will auto-check everything: +Validation will auto-check everything and return the results as data frames. You can find more information abou the specific checks by reading `vignette("validation", package = "pegboard")`. + +Details of the individual functions can be found via `?validate_links()`, +`?validate_divs()`, and `?validate_headings()`. ```{r validate} -wb_lesson$validate_divs() -wb_lesson$validate_headings() -wb_lesson$validate_links() +divs <- wb_lesson$validate_divs() +print(divs) +headings <- wb_lesson$validate_headings() +print(headings) +links <- wb_lesson$validate_links() +print(links) ``` ## Loading Built Documents @@ -212,6 +236,35 @@ output into a single file and prepends the Episode title before each section: writeLines(wb_lesson$handout()) ``` +## Accessing other `Episode` methods + +For `pegboard::Episode` methods that are not listed above, you will need to +manually iterate over the `Episode` objects. For example, if you wanted to +extract all of the instructor notes in the lesson, you could use `purrr::map()` + +```{r callouts} +purrr::map(c(wb_lesson$episodes, wb_lesson$extra), + function(ep) ep$get_divs("instructor")) +``` + +If you wanted to get a specific thing from the body of the document, then you +could use any of the functions from {xml2} such as `xml2::xml_find_first()` or +`xml2::xml_find_all()`. Here, we are looking first the first text element that +is not a fenced-div element: + +```{r body-text} +purrr::map_chr(c(wb_lesson$episodes, wb_lesson$extra), + function(ep) { + xpath <- ".//md:text[not(starts-with(text(), ':::'))]" + nodes <- xml_find_first(ep$body, xpath, ep$ns) + return(xml_text(nodes)) + } +) +``` + +For more information about constructing XPath queries and working with XML data, +you can read `vignette("intro-xml", package = "pegboard")` + ## Creating a New Lesson with Child Documents @@ -314,3 +367,13 @@ for determining if anything went wrong with parsing the kramdown syntax, the `$isolate_blocks()` method was used to strip out all non-blockquote content, the `$blocks()` method returned all block quote with filters for types, and the `$rmd` field was an indicator that the lesson used R Markdown. + + +```{r jekyll} +jekyll <- Lesson$new(lesson_fragment("lesson-fragment"), jekyll = TRUE) +jekyll$n_problems +rmd <- Lesson$new(lesson_fragment("rmd-lesson"), jekyll = TRUE) +rmd$n_problems +``` + + From 333272f070eb81fa04ce88f285778645f110eb01 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Tue, 21 Nov 2023 11:31:17 -0800 Subject: [PATCH 13/16] bump to devel; add NEWS --- DESCRIPTION | 2 +- NEWS.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 71051cc2..e2f7d2d6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: pegboard Title: Explore and Manipulate Markdown Curricula from the Carpentries -Version: 0.7.2 +Version: 0.7.2.9000 Authors@R: c( person(given = "Zhian N.", family = "Kamvar", diff --git a/NEWS.md b/NEWS.md index 6bfc2911..f308916d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,12 @@ +# pegboard 0.7.2.9000 (unreleased) + +## DOCUMENTATION + +* New `vignette("intro-lesson", package = "pegboard")` provides information + about working with `pegboard::Lesson` objects. +* Documentation for `Episode` and `Lesson` objects have been updated to be + a bit more descriptive and point to the vignettes. + # pegboard 0.7.2 (2023-11-17) ## MISC From 17a8bc26eba674a34af0cc0a212cb800fa9b3378 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Wed, 22 Nov 2023 10:45:19 -0800 Subject: [PATCH 14/16] add force argument for unblock --- R/Episode.R | 6 ++++-- man/Episode.Rd | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/R/Episode.R b/R/Episode.R index ffbe5f24..430c876f 100644 --- a/R/Episode.R +++ b/R/Episode.R @@ -488,6 +488,8 @@ Episode <- R6::R6Class("Episode", }, #' @description convert challenge blocks to roxygen-like code blocks #' @param token the token to use to indicate non-code, Defaults to "#'" + #' @param force force the conversion even if the conversion has already + #' taken place #' @return the Episode object, invisibly #' @examples #' loop <- Episode$new(file.path(lesson_fragment(), "_episodes", "14-looping-data-sets.md")) @@ -496,8 +498,8 @@ Episode <- R6::R6Class("Episode", #' loop$unblock() #' loop$get_blocks() # no blocks #' loop$code # now there are two blocks with challenge tags - unblock = function(token = "#'") { - if (private$mutations['unblock']) { + unblock = function(token = "#'", force = FALSE) { + if (!force && private$mutations['unblock']) { return(invisible(self)) } if (private$mutations['use_dovetail']) { diff --git a/man/Episode.Rd b/man/Episode.Rd index 4e0d8942..ff3b70b2 100644 --- a/man/Episode.Rd +++ b/man/Episode.Rd @@ -799,13 +799,16 @@ scope$isolate_blocks()$body # only one challenge block_quote \subsection{Method \code{unblock()}}{ convert challenge blocks to roxygen-like code blocks \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Episode$unblock(token = "#'")}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Episode$unblock(token = "#'", force = FALSE)}\if{html}{\out{
}} } \subsection{Arguments}{ \if{html}{\out{
}} \describe{ \item{\code{token}}{the token to use to indicate non-code, Defaults to "#'"} + +\item{\code{force}}{force the conversion even if the conversion has already +taken place} } \if{html}{\out{
}} } From 9eeb69d9329562cde143a45044f9d0490127b411 Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Wed, 22 Nov 2023 10:45:34 -0800 Subject: [PATCH 15/16] add documentation for use_sandpaper --- R/use_sandpaper.R | 14 +++++++++++++- man/use_sandpaper.Rd | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 man/use_sandpaper.Rd diff --git a/R/use_sandpaper.R b/R/use_sandpaper.R index 70c3bf59..5265c0a6 100644 --- a/R/use_sandpaper.R +++ b/R/use_sandpaper.R @@ -1,5 +1,17 @@ #' Convert a Jekyll-based lesson to a sandpaper-based lesson #' +#' @details A Jekyll episode is littered with kramdown inline attribute tags +#' and liquid-formatted links. Converting to sandpaper means the following: +#' +#' - links using liquid formatting (e.g. `[text]({{ site.path }}/01-episode/)` +#' are replaced with their relative counterparts (`[text](01-episode.md)`. +#' - include statements for `links.md` and `base_path.md` are removed +#' - image attributes have the kramdown `:` removed +#' - code blocks with a kramdown inline attribute tag are converted to +#' commonmark via the internal [liquid_to_commonmark()]. +#' - Lesson template-specific code is removed from the setup chunk in R +#' Markdown files. +#' #' @param body the xml body of an episode #' @param rmd if `TRUE`, the chunks will be converted to RMarkdown chunks #' @param yml a list derived from the `_config.yml` file that defines the site @@ -9,7 +21,7 @@ #' @param known_paths a character vector with the known paths in the lesson. #' This is used to determine the correct path to other files in the lesson. #' @return the body -#' @noRd +#' @keywords internal use_sandpaper <- function(body, rmd = TRUE, yml = list(), path = NULL, known_paths = NULL) { if (inherits(body, "xml_missing")) { warning("episode body missing", call. = FALSE) diff --git a/man/use_sandpaper.Rd b/man/use_sandpaper.Rd new file mode 100644 index 00000000..536030b9 --- /dev/null +++ b/man/use_sandpaper.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/use_sandpaper.R +\name{use_sandpaper} +\alias{use_sandpaper} +\title{Convert a Jekyll-based lesson to a sandpaper-based lesson} +\usage{ +use_sandpaper(body, rmd = TRUE, yml = list(), path = NULL, known_paths = NULL) +} +\arguments{ +\item{body}{the xml body of an episode} + +\item{rmd}{if \code{TRUE}, the chunks will be converted to RMarkdown chunks} + +\item{yml}{a list derived from the \verb{_config.yml} file that defines the site +variables.} + +\item{path}{the path to the source of the body. Defaults to NULL. This is +used in conjunction with \code{known_paths}} + +\item{known_paths}{a character vector with the known paths in the lesson. +This is used to determine the correct path to other files in the lesson.} +} +\value{ +the body +} +\description{ +Convert a Jekyll-based lesson to a sandpaper-based lesson +} +\details{ +A Jekyll episode is littered with kramdown inline attribute tags +and liquid-formatted links. Converting to sandpaper means the following: +\itemize{ +\item links using liquid formatting (e.g. \verb{[text](\{\{ site.path \}\}/01-episode/)} +are replaced with their relative counterparts (\verb{[text](01-episode.md)}. +\item include statements for \code{links.md} and \code{base_path.md} are removed +\item image attributes have the kramdown \code{:} removed +\item code blocks with a kramdown inline attribute tag are converted to +commonmark via the internal \code{\link[=liquid_to_commonmark]{liquid_to_commonmark()}}. +\item Lesson template-specific code is removed from the setup chunk in R +Markdown files. +} +} +\keyword{internal} From 4d7b11e5d5454cd3e96fde490e7e61c964b8427f Mon Sep 17 00:00:00 2001 From: "Zhian N. Kamvar" Date: Wed, 22 Nov 2023 10:45:48 -0800 Subject: [PATCH 16/16] update episode and lesson vignettes --- NEWS.md | 5 +++ vignettes/intro-episode.Rmd | 71 +++++++++++++++++++++++++++++++++---- vignettes/intro-lesson.Rmd | 46 ++++++++++++++++++++---- 3 files changed, 109 insertions(+), 13 deletions(-) diff --git a/NEWS.md b/NEWS.md index f308916d..da988c7d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,11 @@ * Documentation for `Episode` and `Lesson` objects have been updated to be a bit more descriptive and point to the vignettes. +## NEW FEATURES + +* `Episode$unblock()` method gains the `force` argument which will allow the + method to run even if it has previously been run. + # pegboard 0.7.2 (2023-11-17) ## MISC diff --git a/vignettes/intro-episode.Rmd b/vignettes/intro-episode.Rmd index a546eb37..54cecbf5 100644 --- a/vignettes/intro-episode.Rmd +++ b/vignettes/intro-episode.Rmd @@ -10,7 +10,7 @@ vignette: > ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, - comment = "#>" + comment = "##" ) ``` @@ -417,7 +417,6 @@ ep$tags xml2::xml_parent(ep$tags) ``` - ### Transformation It was always known that we would want to use a different syntax to write the @@ -426,13 +425,16 @@ was difficult to parse and validate. The automated transformation workflow is what powers the Lesson Transformation Tool and we have composed it into a few basic steps: -1. transform block quotes to fenced divs -2. removing the jekyll syntax, liquid templating, and fix relative links -3. moving the yaml frontmatter +1. transform block quotes to fenced divs (via the `$unblock()` method, using + the internal `replace_with_div()` function). +2. removing the jekyll syntax, liquid templating, and fix relative links (via + the `$use_sandpaper()` method, using the internal `use_sandpaper()` + function. +3. moving the yaml frontmatter using the `$move_` methods The process looks like this composable chain of methods: -```{r} +```{r transform} ep$reset() ep$ unblock()$ @@ -445,3 +447,60 @@ ep$tail(65) ``` +#### Transformation tip! + +There are times where the lesson authors forget to tag a block quote with the +correct type of tag to make it a callout block. In this case, the block quote +will remain unconverted in the lesson. For example let's say there was a block +quote at the very top of the lesson that defined prerequisites, but the author accidentally had a new line between the blockquote and the IAL tag, meaning that +the blockquote was not processed: + +```{r transform-prereq} +# add a prerequisite block after the questions and objectives +untranslated_block <- " +> ## Barnaby +> +> - a barn +> - a bee +> + +{: .prereq} +" +ep$add_md(untranslated_block, where = 10) +ep$head(31) +``` + +Notice that the block quote is picked up only as a blockquote, but not a +_special_ block quote. + +```{r block-head} +ep$get_blocks(".prereq") +ep$get_blocks() +``` + +If we set the `ktag` attribute of this block quote to "{: .prereq}", then it +will be recognised as a special blockquote, which can be translated. + +```{r block-set} +# remove the orphan prereq tag: +orphan_tag <- xml2::xml_find_all(ep$body, + ".//md:paragraph[md:text[contains(text(),'.prereq}')]]", ns = ep$ns) +xml2::xml_remove(orphan_tag) +ep$head(31) + +# set the attribute of the block quote: +xml2::xml_set_attr(ep$get_blocks(), "ktag", "{: .prereq}") +ep$head(31) +ep$get_blocks(".prereq") +``` + +Now we can convert the block to a fenced div with `$unblock()` + +```{r block-unblock} +ep$unblock(force = TRUE)$head(31) +``` + + + + + diff --git a/vignettes/intro-lesson.Rmd b/vignettes/intro-lesson.Rmd index 1e0717dc..a404f319 100644 --- a/vignettes/intro-lesson.Rmd +++ b/vignettes/intro-lesson.Rmd @@ -10,7 +10,7 @@ vignette: > ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, - comment = "#>" + comment = "##" ) ``` @@ -32,8 +32,10 @@ understand the methods that come from the `Episode` objects. In this vignette, we will talk about the structure of `Lesson` objects, how to use the basic methods of these objects, inspecting summaries of source vs built episodes, and assessing the lineage of episodes that have parents and/or children documents. -But first, because of a default parameter, I need to explain a little bit about -Jekyll, the former lesson infrastructure. + +But first, because of a default parameter that influences what methods can be +used depending on the lesson context, I need to explain a little bit about +Jekyll, the former lesson infrastructure. ### A Brief Word About History and Jekyll @@ -86,10 +88,6 @@ to the new style, thus we added the `jekyll` parameter to the `Lesson` object initializer method, and set `jekyll = TRUE` as the default to keep backwards compatibility. -#### Syntax - -```{r child="../man/rmd-fragments/jekyll-syntax.Rmd"} -``` ## Creating a New Lesson Object @@ -361,6 +359,20 @@ glue("The lineage of {path_rel(parent, start = rel)} is: ## Jekyll Lessons +This section will talk about the peculiarities with lessons built with the +[carpentries/styles](https://carpentries.github.io/lesson-example/) lesson +template. Note that lesson transition methods are not implemented in the +`Lesson` object, if you want to find out about methods for transition, please +read the Jekyll section in `vignette("intro-episode", package = "pegboard")`. + + +### Syntax + +```{r child="../man/rmd-fragments/jekyll-syntax.Rmd"} +``` + +### Methods + There are some methods that are specific to lessons that are built with Jekyll. In particular, the `n_problems` and `show_problems` active binding are useful for determining if anything went wrong with parsing the kramdown syntax, @@ -376,4 +388,24 @@ rmd <- Lesson$new(lesson_fragment("rmd-lesson"), jekyll = TRUE) rmd$n_problems ``` +As mentioned above, in Jekyll uses special block quotes to format callout +blocks. The `$challenges()` and `$solutions()` methods recognise this and will +return the block quotes: + +```{r jeky-chal} +jekyll$challenges() +jekyll$solutions() +``` + +For other blocks, you can use the `$blocks()` method: + +```{r jeky-blocks} +jekyll$blocks(".prereq") +rmd$blocks(".prereq") +``` + + + + +