Skip to contents

When handling child files for lessons, it is important that changes in child files will cause the source file to change as well.


hash_with_children(checksums, files, children, root_path)




the hashes of the parent files


the relative path of the parent files to the root_path


a named list of character vectors specifying the child files for each parent file in order of appearance. These paths are relative to the folder of the parent file.


the root path to the lesson.


a pegboard::Lesson object


  • get_child_files() a named list of charcter vectors specifying the child files within files in a lesson.

  • hash_with_children() a character vector of hashes of the same length as the parent files.


  • The get_child_files() function finds the child files from a pegboard::Lesson object.

  • Because we use a text database that relies on the hash of the file to determine if a file should be rebuilt, hash_with_children() piggybacks on this paradigm by assigning a unique hash to a parent file with children that is the hash of the vector of hashes of the files. The hash of hashes is created with rlang::hash().


# This demonstration will show how a temporary database can be set up. It
# will only work with a sandpaper lesson
# setup -----------------------------------------------------------------
# The setup needs to include an R Markdown file with a child file.
tmp <- tempfile()
on.exit(fs::dir_delete(tmp), add = TRUE)
#> Error: [ENOENT] Failed to search directory '/tmp/RtmpUWqX2P/file18807b1194a0': no such file or directory
create_lesson(tmp, rmd = FALSE, open = FALSE)
#> → Creating Lesson in /tmp/RtmpUWqX2P/file18807b1194a0...
#>  No schedule set, using Rmd files in episodes/ directory.
#> → Creating Lesson in /tmp/RtmpUWqX2P/file18807b1194a0...

#> To remove this message, define your schedule in config.yaml or use `set_episodes()` to generate it.
#> → Creating Lesson in /tmp/RtmpUWqX2P/file18807b1194a0...

#> ────────────────────────────────────────────────────────────────────────
#> → Creating Lesson in /tmp/RtmpUWqX2P/file18807b1194a0...

#>  To save this configuration, use
#> set_episodes(path = path, order = ep, write = TRUE)
#> → Creating Lesson in /tmp/RtmpUWqX2P/file18807b1194a0...

#>  First episode created in /tmp/RtmpUWqX2P/file18807b1194a0/episodes/
#> → Creating Lesson in /tmp/RtmpUWqX2P/file18807b1194a0...

#>  Workflows up-to-date!
#> → Creating Lesson in /tmp/RtmpUWqX2P/file18807b1194a0...

#>  Lesson successfully created in /tmp/RtmpUWqX2P/file18807b1194a0
#> → Creating Lesson in /tmp/RtmpUWqX2P/file18807b1194a0...

#> /tmp/RtmpUWqX2P/file18807b1194a0
# get namespace to use internal functions
sp <- asNamespace("sandpaper")
db <- fs::path(tmp, "site/built/md5sum.txt")
resources <- fs::path(tmp, c("episodes/", ""))
# create child file
writeLines("Hello from another file!\n",
  fs::path(tmp, "episodes", "files", ""))
# use child file
cat("\n\n```{r child='files/'}\n```\n",
  file = resources[[1]], append = TRUE)
# convert to Rmd
fs::file_move(resources[[1]], fs::path_ext_set(resources[[1]], "Rmd"))
resources[[1]] <- fs::path_ext_set(resources[[1]], "Rmd")
set_episodes(tmp, fs::path_file(resources[[1]]), write = TRUE)

# get_child_files ------------------------------------------------------
# we can get the child files by scanning the Lesson object
lsn <- sp$this_lesson(tmp)
#> [1] "Lesson" "R6"    
children <- sp$get_child_files(lsn)
#> $introduction.Rmd
#> [1] "files/"

# hash_with_children ---------------------------------------------------
# get hash of parent
phash <- tools::md5sum(resources[[1]])
rel_parent <- fs::path_rel(resources[[1]], start = tmp)
sp$hash_with_children(phash, rel_parent, children, tmp)
#>          episodes/introduction.Rmd 
#> "f0803606fb41fb5ba9d03df618d492ae" 
# demonstrate how this works ----------------
# the combined hashes have their names removed and then `rlang::hash()`
# creates the hash of the unnamed hashes.
chash <- tools::md5sum(fs::path(tmp, "episodes", children[[1]]))
hashes <- unname(c(phash, chash))
#> [1] "4c6b9769ec953e27d83c6c707d549b82"
#> [2] "a649c757324fc9c289e38f3d77c206d5"
#> [1] "f0803606fb41fb5ba9d03df618d492ae"