jwebsite/code/eval-tricks.md

9.0 KiB

@def hascode=true

Tricks with code evaluation

\blurb{Franklin's recursive nature coupled with code evaluation allows for neat and useful tricks.}

\lineskip

\toc

The basic idea is to exploit the fact that the output of a Julia code block evaluated by Franklin can be re-processed as Franklin Markdow when using the \textoutput command; this offers a wide range of possibilities best shown through a few examples (more or less in increasing degree of sophistication).

Generating a table

Preview

#hideall
names = (:Taimur, :Catherine, :Maria, :Arvind, :Jose, :Minjie)
numbers = (1525, 5134, 4214, 9019, 8918, 5757)
println("@@simple-table")
println("Name | Number")
println(":--- | :---")
println.("$name | $number" for (name, number) in zip(names, numbers))
println("@@")
raw"""
~~~
<style>
.simple-table tr {
  padding:0;
  line-height:1em;
}
</style>
~~~
""" |> println

\textoutput{table}

Code

That can be obtained with:

```julia:table
#hideall
names = (:Taimur, :Catherine, :Maria, :Arvind, :Jose, :Minjie)
numbers = (1525, 5134, 4214, 9019, 8918, 5757)
println("Name | Number")
println(":--- | :---")
println.("$name | $number" for (name, number) in zip(names, numbers))
```

\textoutput{table}

The code block will be executed and not shown (#hideall) generating a table line by line. In practice, the code generates the markdown

Name | Number
:--- | :---
Bob | 1525
...
Minjie | 5757

which is captured and reprocessed by the \textoutput command.

This can be used effectively when combined with reading data files etc. and of course you could do further CSS styling of the table.

Colourful circles

The trick can be used to generate SVG code too.

Preview

\newcommand{\circle}[1]{}

#hideall
colors=(:pink, :lightpink, :hotpink, :deeppink, :mediumvioletred, :palevioletred, :coral, :tomato, :orangered, :darkorange, :orange, :gold, :yellow)
print("@@ccols ")
print.("\\circle{$c}" for c in colors)
println("@@")

\textoutput{circles}

Code

That can obtained with (see detailed explanations further below)

\newcommand{\circle}[1]{
  ~~~
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4 4">
  <circle cx="2" cy="2" r="1.5" fill="#1"/></svg>
  ~~~
}

and

```julia:circles
#hideall
colors=(:pink, :lightpink, :hotpink, :deeppink,
        :mediumvioletred, :palevioletred, :coral,
        :tomato, :orangered, :darkorange, :orange,
        :gold, :yellow)
print("@@ccols ")
print.("\\circle{$c}" for c in colors)
println("@@")
```
\textoutput{circles}

The first part defines a command \circle which takes one argument for the fill colour and inserts SVG code for a circle with that colour.

The second part is a Julia code block which will be evaluated but not displayed on the page (since there is a #hideall). The code loops over the each colour c and prints \circle{c} so that the code block effectively generates:

@@ccols \circle{pink}...\circle{yellow}@@

this output is then captured and reprocessed with the \textoutput{snippet} command.

The last thing to do is to style the colors div appropriately:

.ccols {
  margin-top:1.5em;
  margin-bottom:1.5em;
  margin-left:auto;
  margin-right:auto;
  width: 60%;
  text-align: center;}
.ccols svg {
  width:30px;}

Team cards

You may want to have a page with responsive team cards for instance where every card would follow the same layout but the content would be different. There are multiple ways you can do this with Franklin and a simple one below (adapted from this tutorial). The advantage of doing something like this is that it can help separate the content from the layout making both arguably easier more maintainable.

Preview

\newcommand{\card}[5]{ @@card #1 @@container ~~~

#1

~~~ @@title #3 @@ @@vitae #4 @@ @@email #5 @@ ~~~

Contact

~~~ @@ @@ }

#hideall
team = [
  (name="Jane Doe", pic="beth", title="CEO & Founder", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com"),
  (name="Mike Ross", pic="rick", title="Art Director", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com"),
  (name="John Doe", pic="meseeks", title="Designer", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com")
  ]

"@@cards @@row" |> println
for person in team
  """
  @@column
    \\card{$(person.name)}{$(person.pic)}{$(person.title)}{$(person.vitae)}{$(person.email)}
  @@
  """ |> println
end
println("@@ @@") # end of cards + row

raw"""
~~~
<style>
.column {
  float:left;
  width:30%;
  margin-bottom:16px;
  padding:0 8px;
}
@media (max-width:62rem) {
  .column {
    width:45%;
    display:block;
  }
}
@media (max-width:30rem){
  .column {
    width:95%;
    display:block;
  }
}
.card{
  box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
}
.card img {
  padding-left:0;
  width: 100%;
}
.container {
  padding: 0 16px;
}
.container::after, .row::after{
  content:"";
  clear:both;
  display:table;
}
.title {
  color:grey;
}
.vitae {
  margin-top:0.5em;
}
.email {
  font-family:courier;
  margin-top:0.5em;
  margin-bottom:0.5em;
}
.button{
  border:none;
  outline:0;
  display:inline-block;
  padding:8px;
  color:white;
  background-color:#000;
  text-align:center;
  cursor:pointer;
  width:100%;
}
.button:hover{
  background-color:#555;
}
</style>
~~~
""" |> println

\textoutput{teamcards}

Code

In order to do this you could first define a \card command:

\newcommand{\card}[5]{
  @@card
    ![#1](/assets/img/team/!#2.jpg)
    @@container
      ~~~
      <h2>#1</h2>
      ~~~
      @@title #3 @@
      @@vitae #4 @@
      @@email #5 @@
      ~~~
      <p><button class="button">Contact</button></p>
      ~~~
    @@
  @@
}

And then use it in a loop that goes over each person:

```julia:teamcards
#hideall
team = [
  (name="Jane Doe", pic="beth", title="CEO & Founder", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com"),
  (name="Mike Ross", pic="rick", title="Art Director", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com"),
  (name="John Doe", pic="meseeks", title="Designer", vitae="Phasellus eget enim eu lectus faucibus vestibulum", email="example@example.com")
  ]

"@@cards @@row" |> println
for person in team
  """
  @@column
    \\card{$(person.name)}{$(person.pic)}{$(person.title)}{$(person.vitae)}{$(person.email)}
  @@
  """ |> println
end
println("@@ @@") # end of cards + row
```

\textoutput{teamcards}

That's about it! though of course a bit of CSS styling is needed such as:

.column {
  float:left;
  width:30%;
  margin-bottom:16px;
  padding:0 8px; }
@media (max-width:62rem) {
  .column {
    width:45%;
    display:block; }
  }
@media (max-width:30rem){
  .column {
    width:95%;
    display:block;}
  }
.card { box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); }
.card img {
  padding-left:0;
  width: 100%; }
.container { padding: 0 16px; }
.container::after, .row::after{
  content: "";
  clear: both;
  display: table; }
.title { color: grey; }
.vitae { margin-top: 0.5em; }
.email {
  font-family: courier;
  margin-top: 0.5em;
  margin-bottom: 0.5em; }
.button{
  border: none;
  outline: 0;
  display: inline-block;
  padding: 8px;
  color: white;
  background-color: #000;
  text-align: center;
  cursor: pointer;
  width: 100%; }
.button:hover{ background-color: #555; }

Python code blocks

Using PyCall.jl you can evaluate Python code in Julia, and so you can do that in Franklin too. The code below could definitely be improved and generalised but the point here is to show how things can work together. You could adapt this to work with something like RCall.jl as well.

\newcommand{\pycode}[2]{

#hideall
using PyCall
lines = replace("""!#2""", r"(^|\n)([^\n]+)\n?$" => s"\1res = \2")
py"""
$$lines
"""
println(py"res")
#2

\codeoutput{!#1} }

\pycode{py1}{ import numpy as np np.random.seed(2) x = np.random.randn(5) r = np.linalg.norm(x) / len(x) np.round(r, 2) }

Code

We first define a \pycode command that takes lines of python code, adds a res = before the last line, runs the lines and finally show res. It also inputs the lines of code in a fenced block.

\newcommand{\pycode}[2]{
```julia:!#1
#hideall
using PyCall
lines = replace("""!#2""", r"(^|\n)([^\n]+)\n?$" => s"\1res = \2")
py"""
$$lines
"""
println(py"res")
```
```python
#2
```
\codeoutput{!#1}
}

calling the command is straightforward:

\pycode{py1}{
  import numpy as np
  np.random.seed(2)
  x = np.random.randn(5)
  r = np.linalg.norm(x) / len(x)
  np.round(r, 2)
}