Skip to content

Conversation

@gadenbuie
Copy link
Member

@gadenbuie gadenbuie commented Aug 16, 2021

Fixes #570 and refactors the exercise and question cache env into a single object in a tutorial_cache_env.

Tutorial Item Cache

We now store exercises and questions in list in cache env rather than two separate envs:

  • Bring questions and exercises into an objects list in a single env tutorial_cache_env
  • exercise objects now have a tutorial_exercise class
  • Refactor getters and setters as wrappers around get_tutorial_cache() and set_tutorial_cache()

The objects in the tutorial cache are now also exposed in the $items element of get_tutorial_info(). This lets authors access the list of items in a tutorial with their associated order, type, label and underlying data.

Tutorial State

By storing the exercise and question objects in a named list populated by order of appearance in the tutorial, we can now return the tutorial state in order. When label = NULL in get_tutorial_state(), the state is returned as a list in order of appearance of the interactive elements. This call will still take a reactive dependency on all questions/exercises, but users can access specific questions/exercises by providing the label argument.

Exercise Objects

Finally, I added gave the exercise objects a class -- tutorial_exercise to parallel the existing tutorial_question class. This class gains a print method that's a small wrapper around the exercise chunk formatting functions that are used for writing the exercise temp Rmd. I updated mock_exercise() to use this formatter as well:

mock_exercise(
  user_code = "z <- 3",
  chunks = list(
    mock_chunk("setup-1", "x <- 1"),
    mock_chunk("setup-2", "y <- 2", exercise.setup = "setup-1")
  ),
  setup_label = "setup-2"
)
```{r "setup-1"}
x <- 1
```

```{r "setup-2", exercise.setup="setup-1"}
y <- 2
```

```{r "ex", exercise.setup="setup-2", exercise=TRUE}
z <- 3
```

Internal Changes

Internally, I removed the finicky knitr source hook that was used to store the global setup chunk as a special object in the tutorial cache. Now, rather than handling it with a special knitr hook — which was brittle, esp. when the tutorial is rendered interactively — I've simply in-lined the global setup code into the exercise object. This makes the exercise object more consistent and less reliant on process steps that only happen in the Shiny observer handling exercise submissions.

A knock-on effect from this change is that we now have better handlers for the special setup-global-exercise chunk, which is a separate setup chunk that can provide setup code for all exercises. This chunk replaces the global setup chunk, although for learnr tutorials that are run locally the setup-global-exercise chunk is effectively an additional setup chunk for each exercise. As a result, we force evaluation of the exercise$global_setup code when we know that it came from the setup-global-exercise chunk. (Since this relies on attributes, we assume that any evaluator that serializes the exercise will know to set evaluate_global_setup = TRUE when calling evaluate_exercise().)

Finally, I also added an internal helper — prepare_tutorial_cache_from_source() — that will let us better test the results of the rmarkdown::render() step. These kinds of tests are very much needed but missing in our current test suite. A happy side-effect is that this function can be used to read the exercise/question objects from a tutorial without having to run the tutorial.

- Bring questions and exercises into an `objects` list in a single env `tutorial_cache_env`
- Give `exercise` objects a `learnr_exercise` class
- Refactor getters and setters to use `get_tutorial_cache()` and `set_tutorial_cache()`
- Handle "__setup__" chunk directly in knitr hooks
When `label = NULL` in `get_tutorial_state()`, the state is returned as a list in order of appearance of the interactive elements
With custom format method to align appearance with typical exercise objects
@gadenbuie gadenbuie requested a review from schloerke August 16, 2021 16:31
@gadenbuie gadenbuie force-pushed the 570-order-tutorial-state branch from fc736b2 to 062efe7 Compare August 16, 2021 16:43
Previously we tried to store the global setup in a separate shiny prerendered chunk with the name "__setup__".
This change avoids potential exercise id clashes with a user-created chunk labelled "__setup_-".
It also avoids a tricky knitr source hook issue when calling `rmarkdown::run()` interactively that would cause the global setup chunk to be lost.
I'd prefer `learnr_exercise` but questions are super-classed `tutorial_question` (and have been for a while)
- Treat as exercise support
- Force evaluation of `global_setup` if used in exercises
- Add related tests
@gadenbuie gadenbuie requested a review from schloerke August 18, 2021 15:42
it shouldn't be evaluated locally, but it should be used by external evaluators with `evaluate_global_setup = TRUE`
@gadenbuie gadenbuie merged commit 8025f57 into rstudio:master Aug 24, 2021
@gadenbuie gadenbuie deleted the 570-order-tutorial-state branch August 24, 2021 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

get_tutorial_state() returning exercise states in order

2 participants