Skip to content

Commit

Permalink
Change default module
Browse files Browse the repository at this point in the history
  • Loading branch information
rikhuijzer committed Jun 23, 2021
1 parent d353c62 commit 726460d
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 68 deletions.
31 changes: 10 additions & 21 deletions docs/contents/about.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# About {#sec:about}

Basically, this package is a wrapper around [Pandoc](https://pandoc.org/){target="_blank"}; similar to [Bookdown](https://bookdown.org){target="_blank"}.
Note that Pandoc does the heavy lifting and this package adds features on top.
Similar to [Bookdown](https://bookdown.org){target="_blank"} this package is, basically, a wrapper around [Pandoc](https://pandoc.org/){target="_blank"}; similar to [Bookdown](https://bookdown.org){target="_blank"}.
For websites, this package allows for:

- Building a website spanning multiple pages.
Expand All @@ -17,33 +16,23 @@ One of the main differences with Franklin.jl, Weave.jl and knitr (Bookdown) is t
The benefit of this is that you can spawn two separate processes, namely the one to serve your webpages:

```jl
serve_example()
M.serve_example()
```

and the one where you do the computations for your package `Foo`:

```jl
generate_example()
```
$ julia --project -e 'using Books; using Foo; M = Foo'
julia> gen()
[...]
Updating html
```

This way, the website remains responsive when the computations are running.
Thanks to LiveServer.jl and Pandoc, updating the page after changing text or code takes less than a second.
Also, because the `serve` process does relatively few things, it doesn't often crash.
A drawback of this decoupling is that you need to link your text to the correct computation in the Markdown file, whereas in other packages you would insert the code as a string.

The decoupling also allows the output, which you want to include, to be evaluated inside your package, see @sec:embedding-output.
This means that you don't have to define all your dependencies in a `@setup` (Documenter.jl) or `# hideall` (Franklin.jl / Literate.jl) code block.
(Granted, you could work your way around it by only calling methods inside a package.)
The dependencies, such as `using DataFrames`, are available from your package.
This provides all the benefits which Julia packages normally have, such as unit testing and live reloading via Revise.jl.

As another benefit, all the code which you show in a book can be used via the function name.
So, this avoids naming code blocks like "J3" and "J4", and allows users to load the code from your package and call the functions themselves.
This has multiple benefits, namely

1. it allows for explicitly using the output from one code block as input to another code block,
1. it allows for demonstrating to the reader how to organize code (because, in general the tip is: use functions) _and_
1. could save the reader from copy and pasting code.
The decoupling also allows you to have more flexiblity in when you want to run what code.
In combination with Revise.jl, you can quickly update your code and see the updated output.

The latter point is due to the fact that the reader can load the package for the book and run the function.
An example for points 1 and 2 is shown in @sec:function_code_blocks.
10 changes: 5 additions & 5 deletions docs/contents/demo.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,28 @@ For example, to show the Julia version, define a code block like

<pre>
```jl
julia_version()
M.julia_version()
```
</pre>

in a Markdown file.
Then, in your package, define the method `julia_version()`:

```
julia_version() = "This book is built with Julia $VERSION."
M.julia_version() = "This book is built with Julia $VERSION."
```

Next, ensure that you call `using Books; gen(; M)`, where `M = YourModule`.
Alternatively, if you work on a large project and want to only generate the output for one or more Markdown files in `contents/`, such as `index.md`, use

```jl
markdown_gen_example()
M.markdown_gen_example()
```

Calling `gen` will place the text

```jl
julia_version_example()
M.julia_version_example()
```

at the right path so that it can be included by Pandoc.
Expand All @@ -66,7 +66,7 @@ Note that it doesn't matter where you define the function `julia_version`, as lo
To save yourself some typing, and to allow yourself to get some coffee while Julia gets up to speed, you can start Julia for some package `Foo` with

```
$ julia --project -ie 'using Books; using Foo; M = Foo; gen(; M)'
$ julia --project -ie 'using Books; using Foo; M = Foo; gen()'
```

which allows you to re-generate all the content by calling
Expand Down
7 changes: 0 additions & 7 deletions docs/src/includes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,6 @@ serve_example() = code_block(raw"""
""")

generate_example() = code_block(raw"""
$ julia --project -e 'using Books; using Foo; M = Foo'
julia> Books.gen(; M)
Running example() for _gen/example.md
Running julia_version() for _gen/julia_version.md
Running example_plot() for _gen/example_plot.md
Writing plot images for example_plot
[...]
""")

Expand Down
56 changes: 21 additions & 35 deletions src/generate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ function expand_path(p)
end

"""
gen(paths::Vector; M=nothing, fail_on_error=false, project="default")
gen(paths::Vector{String}; M=Main, fail_on_error=false, project="default")
Populate the files in `$(Books.GENERATED_DIR)/` by calling the required methods.
These methods are specified by the filename and will output to that filename.
Expand All @@ -202,55 +202,41 @@ The methods are assumed to be in the module `M` of the caller.
Otherwise, specify another module `M`.
After calling the methods, this method will also call `html()` to update the site when
`call_html == true`.
!!! note
If there is anthing that you want to have available when running the code blocks,
just load them inside your REPL (module `Main`) and call `gen()`.
For example, you can define `M = YourModule` to shorten calls to methods in your module.
"""
function gen(paths::Vector; M=nothing, fail_on_error=false, project="default", call_html=true)
function gen(paths::Vector{String};
M=Main, fail_on_error=false, project="default", call_html=true)
mkpath(GENERATED_DIR)
paths = [contains(dirname(p), "contents") ? p : expand_path(p) for p in paths]
included_expr = vcat([extract_expr(read(path, String)) for path in paths]...)
included_expr = vcat([extract_expr(read(p, String)) for p in paths]...)
f(expr) = evaluate_include(expr, M, fail_on_error)
foreach(f, included_expr)
if call_html
println("Updating html")
html(; project)
end
end
gen(path::String; kwargs...) = gen([path]; kwargs...)

function gen(; M=nothing, fail_on_error=false, project="default", call_html=true)
"""
gen(path::AbstractString; kwargs...)
Convenience method for passing `path::AbstractString` instead of `paths::Vector{AbstractString}`.
"""
function gen(path::AbstractString; kwargs...)
path = string(path)::String
gen([path]; kwargs...)
end

function gen(; M=Main, fail_on_error=false, project="default", call_html=true)
paths = inputs(project)
first_file = first(paths)
if !isfile(first_file)
error("Couldn't find $first_file. Is there a valid project in $(pwd())?")
end
gen(paths; M, fail_on_error, project, call_html)
end

"""
gen(f::Function; fail_on_error=false, project="default", call_html=true)
Populate the file in `Books.GENERATED_DIR` by calling `func`.
This method is useful during development to quickly see the effect of updating your code.
Use with Revise.jl and optionally `Revise.entr`.
After calling `f`, this method will also call `html()` to update the site when `call_html=true`.
# Example
```jldoctest
julia> module Foo
version() = "This book is built with Julia \$VERSION"
end;
julia> call_html = false; # To avoid Pandoc errors breaking this jldoctest.
julia> gen(Foo.version; call_html)
Writing output of `version()` to _gen/version-ob--cb-.md
```
"""
function gen(f::Function; project="default", call_html=true)
path = joinpath(GENERATED_DIR, "$f.md")
mkpath(GENERATED_DIR)
evaluate_and_write(f)
if call_html
println("Updating html")
html(; project)
end
end

0 comments on commit 726460d

Please sign in to comment.