4. Runnable documents
The publisher takes a script and produces a document. This is the other direction: the Markdown document is the source file, with MATLAB living in fenced code blocks, and it runs directly.
## Setting up
```matlab
m = dsge_model('nk');
m = solve(m);
```
The prose is never executed and never has to be commented out, which is the whole point of writing it this way round.
rise.mdrun("round.md") % run the code blocks
rise.publish("round.md") % run it and typeset it
rise.m2md("walkthrough.m") % script -> document
rise.md2m("walkthrough.md") % document -> script
4.1. What runs, and what does not
Only fenced blocks run, and only those whose language is one RISE
recognizes. A fence tagged noexec is shown to the reader and never
executed, which is how a slow, interactive or deliberately broken example
stays in the document:
```matlab noexec
estimate(m, data) % takes an hour; shown, not run
```
Sections narrows the run, matching either a fence tag or the nearest
heading above the block:
rise.mdrun("round.md", Sections="Setting up")
4.2. Where the code runs
This is the option that matters for anything automated.
|
Meaning |
|---|---|
|
the default; the code runs in your workspace, as if you had typed it |
|
a workspace of its own, leaving yours untouched |
a struct |
a workspace of its own, seeded with those variables |
A fresh or seeded run hands its workspace back:
w = rise.mdrun("round.md", Workspace="fresh");
w.peak % what the document computed
Without that a documentation build could execute a page but not look at what the page produced, which is most of the reason to run it. The returned struct carries only what the document assigned.
A seeded run is what makes a long document testable in pieces: run one section, then run the next starting from what the first produced.
setup = rise.mdrun("round.md", Workspace="fresh", Sections="Setting up");
calc = rise.mdrun("round.md", Workspace=setup, Sections="The calculation");
Anything that is neither of the two names nor a struct is refused by
RISE:mdrun:badWorkspace.
Echo prints each block before running it and is off by default, so an
automated build is not buried in the code it is running. DryRun
returns the assembled code without running any of it.
4.3. The round trip
A script and a document are two ways of writing the same material, and the conversion between them is a fixed point: convert, convert back, and convert again, and the second document equals the first.
md = rise.m2md("walkthrough.m");
back = rise.md2m(md);
again = rise.m2md(back); % identical to md
That property is what lets a team keep both forms without watching them drift. It holds by construction rather than by agreement between two functions: whitespace is normalized in the one place both converters pass through, so a run of blank lines collapses to one, trailing space goes, and the ends are trimmed.
Everything the publisher’s grammar carries survives the trip: headings and their depth, a heading’s label, prose with its emphasis and lists, display mathematics, and the code. A label dropped in conversion would be a cross-reference that silently stops resolving, so it is carried and tested.
Neither converter overwrites an existing file. Pass Overwrite=true to
say you meant it, or SaveAs to choose the destination.
4.4. One pipeline, two front ends
rise.publish accepts either form. The script lexer and the Markdown
lexer produce the same typed block list, and everything after that is
shared: execution, output capture, figure export and the LaTeX backend.
So a document and the script it converts to give the same PDF, and a new output format is written once rather than twice.
Worked example:
rise-modern-tutorials/Reporting/runnable_documents.
See also