静的サイトジェネレーターDapper入門： レイアウトファイル内のフロー制御


# 要約

**Dapperによるサイトデータ構築において、HTMLページの「型紙（テンプレート）」となるのがレイアウトファイルです。レイアウトファイルには「制御構造」をもたせることができます。制御構造には、条件分岐・ループなどがあり、これらを利用すると、状況に応じて柔軟に最終HTMLファイルの文書構成が変化するような柔軟性ある「型紙」が構築できます。**

# Dapperおさらい

[前記事](https://qiita.com/doikoji/items/7ce1ce186b74bf9f4c75)では、レイアウトファイルの書き方、特に、変数・リスト・ハッシュの基本的な扱い方について解説しました。

Dapperでは、「型紙」にあたるレイアウトファイルに、記事ごとの独自データを書き込んだ「ソースファイル」の内容を組み合わせて、公開用のhtmlファイルを作り出します。レイアウトファイルはところどころに「穴」が開けてあり、その穴を記事データファイルの内容で埋めていくわけです。そして、複雑な構造を持つ入力データからHTMLを紡ぎ出すのには、データをリストやハッシュとして取り扱うのが有用であることを見ました。

# はじめに

引き続き、静的サイトジェネレーターDapperのレイアウトファイルの書き方を解説します。本記事では、「[制御構造](https://ja.wikipedia.org/wiki/%E5%88%B6%E5%BE%A1%E6%A7%8B%E9%80%A0)」について纏めます。

レイアウトファイルに制御構造を導入することによって、記事の内容をよりダイナミックに切り替えることができます。変数の値に応じて単語を差し替えるといったレベルではなく、文章構造をそっくり差し替えてしまうことさえ可能です。また、リストの内容に応じてその都度項目数が変わるような内容に対応するためにも、制御構造の活用が欠かせません。

# 条件によって処理内容を切り替える（if-then-elseブロック）

## if ディレクティブ
```
<p>
[% if a==1 %]
aの値は1
[% else %]
aの値は1以外
[% end %]
</p>
```
`if`ディレクティブを使うと、評価式の値に応じて出力テキストの内容を切り替えたりできます。`else`ディレクティブは必要なければ省略して構いません。

上の例では、変数`a`の値が1の場合とそうでない場合とで2種類の出力が切り替えられます。

## unless ディレクティブ
Dapperにはperlの文法に準拠した`unless`ディレクティブも用意されています。英単語の意味から類推できるとおり、評価式が「偽」の場合に実行されるブロックを作ります。つまり`if not`ですね。

変数の値が定義されている場合のみテキストを追加するなどというありがちな処理に便利でしょう。

```
[% unless errormessage eq "" %]
<p>エラー：[% errormessage %]</p>
[% end %]
```

上の例では、`errormessage`に何らかのテキストが定義されている場合に限り、その内容が`p`タグでくくった形で出力されます。

## 比較演算子（==, <, >, <=, >=, eq, lt, gt, le, ge)を使い分ける

さて、Dapperでは演算処理に[Template::Alloy::Operator](https://metacpan.org/pod/distribution/Template-Alloy/lib/Template/Alloy/Operator.pm)というモジュールを使っており、基本的には素のperlに限りなく近い形で演算子を使うことができます。

perlの比較演算子は２種類に分類できます。数値対象の演算子と文字列対象の演算子です。

| 比較対象同士の関係性  | 数値対象の比較式 | 文字列対象の比較式 |
|:-------------------|:-----------:|:--------------:|
| AとBは等しい        |  A == B     |   A eq B       |
| AはBより大きい       | A > B       |   A gt B       |
| AはBより大きいか等しい | A >= B     |   A ge B       |
| AはBより小さい        | A < B      |   A lt B       |
| AはBより小さいか等しい | A <= B      |   A le B       |

同じデータを対象としても、数値比較と文字列比較では結果が異なる場合があります。

* 数値としての比較：`"0"=="0.0"`、`"010"=="10"`はいずれも真となり、`"2">="10"`は偽となる。
* 文字列としての比較：`"0" eq "0.0"`、"010" eq "10"`はいずれも偽となり、`"2" gt "10"`は真となる。


つまり、データの比較を「数値」として行いたいなら`[% if a == b %]`のように数値比較演算子を使い、「文字列」として行いたいなら`[% if a eq b %]`のように文字列比較演算子を使わなければなりません。perl遣いには常識ですが、他の言語ではそういった使い分けをしないものが多いので、戸惑うかもしれません。

本家マニュアルでも使い分けが曖昧で、明らかに`eq`を使うべきところで`==`を使った例が示されている箇所がありますから、注意して読んでください。

# リストの要素ごとに処理を行う（forブロック、forループ）

```
[% x = ["a","b","c","d","e"] %]
<ul>
[% for item in x %]
<li>[% item %]</li>
[% end %]
</ul>
```
上の例は、対象リスト(x)の要素の数だけ`<li>`を使って箇条書き表示を実現します。

よくある`for i=0; i<=n; i++`のような構文はサポートされていないようです。これは、次に述べるwhileブロックで対応できます。

# 条件が満たされている限り繰り返し処理を行う（whileブロック、whileループ）

```
[% i = 0 %]
[% while i < 3 %]
    [% i = i + 1 %]
    i = [% i %]
[% end %]
```

`while`ディレクティブに書かれている条件が真である限り[% end %]までの部分は繰り返し実行されます。

上の例は、まさに典型的なforループを実現している例です。

# 複数の分岐先を設定した条件分岐（switch/caseブロック）

```
[% switch page.category %]
    [% case "books"  %] Category: Books
    [% case "movies" %] Category: Movies
    [% case default  %] Category: Unknown
[% end %]
```

C言語やJavaScriptがサポートしているのと同様のswitch-case構文もサポートされています。

`switch`ディレクティブの後ろに指定された式の値が`case`ディレクティブで指定した値と合致すれば、その`case`ディレクティブに続いて記述された部分が実行されます。`[% case default %]`は、他のどの`case`ディレクティブとも値が一致しなかった場合に実行されるブロックです。

# 総括

レイアウトファイルとは、公開用HTMLの元となる「型紙」で、その本質は穴埋め問題の問題用紙のようなものです。

本記事で説明したような「制御構造」を活用すれば、単に穴を埋めるだけでなく、ダイナミックに文章構造を調整することができます。

リストを使うなら「ループ」は必要不可欠でしょう。ifブロックについては、異常値が入力された場合の特別な警告文付加とか、言語ごとにテキストやヘッダのパラメータを切り替えたバイリンガルサイト構築などの応用を思いつきます。

工夫次第で表現の幅を大きく広げられることでしょう。