本記事の概要
Hugoのショートコードの入門となる初心者向けの記事です。
本記事はその3になります。
前提
Hugo+Blowfishテーマ適用済みの環境です。
なお、Windowsでの操作を想定しています。
(本記事が属するアドベントカレンダーについてもご確認ください)
また、その1~2の内容が前提となります。
if文について
HugoのShortCodeには他のプログラミング言語のようにif文も存在するので、条件に応じてその内容をかき分けることが出来ます。
前回書いたruby ShortCodeをif文を用いると下記のようになります。
(動きは同じです)
ifの条件を満たせばif内の内容が、満たさない場合はelse内の内容が実行されます。
ただし、この例では同じHTMLの構造を2回書くことになります。
{{- if .IsNamedParams -}}
<ruby>{{- (.Get "rb") -}}<rp>(</rp><rt>{{- (.Get "rt") -}}</rt><rp>)</rp></ruby>
{{- else -}}
<ruby>{{- (.Get 0) -}}<rp>(</rp><rt>{{- (.Get 1) -}}</rt><rp>)</rp></ruby>
{{- end -}}
本記事では紹介しませんが「else if」も存在しますので、複雑な判定も記載できます。
if文と変数の注意点(スコープ:有効範囲)
さて、上のShortCodeでは変数を使わず、パラメーターをべた書きで書いていますが、
前回使用した変数を用いれば下記のように書けるようにも思えます。
つまり、if文の条件が成立した場合と成立しなかった場合でそれぞれ変数を作成し、その後に値を参照すれば同じことが出来そうな気がします。
{{- if .IsNamedParams -}}
{{- $rb := (.Get "rb") -}}
{{- $rt := (.Get "rt") -}}
{{- else -}}
{{- $rb := (.Get 0) -}}
{{- $rt := (.Get 1) -}}
{{- end -}}
<ruby>{{- $rb -}}<rp>(</rp><rt>{{- $rt -}}</rt><rp>)</rp></ruby>
しかし、この状態で実行すると、下記のようにエラーになります。
Error: error building site: "{Hugoの環境のパス}\layouts\shortcodes\ruby.html:8:1": parse of template failed: template: _shortcodes/ruby.html:8: undefined variable "$rb"
上記のエラー文をよく読むと8行目に対して「undefined variable」という指摘がなされています。
簡単に和訳すれば、「rb」という変数は定義されていないと言っています。
しかしながら、ifもしくはelseでrbやrtは定義しているはずです。
ではなぜ起きるのでしょうか?
それは変数の有効範囲(スコープ)があるからです。
簡単に言えば、ifやelse内で変数を宣言する場合、その中でしか使えないのです。
そのため、if文が終わったあとの8行目では変数が使えずエラーになります。
変数の値の上書きについて
逆に言えば、if文の外で変数を宣言すれば、if文内でも使えることになります。
先のShortCodeを動くようにしたものを以下に記載します。
{{- $rb := "" -}}
{{- $rt := "" -}}
{{- if .IsNamedParams -}}
{{- $rb = (.Get "rb") -}}
{{- $rt = (.Get "rt") -}}
{{- else -}}
{{- $rb = (.Get 0) -}}
{{- $rt = (.Get 1) -}}
{{- end -}}
<ruby>{{- $rb -}}<rp>(</rp><rt>{{- $rt -}}</rt><rp>)</rp></ruby>
さて、ここでは :=と=の使い分けについて注目してください。
1~2行目で、変数を宣言する際に初期値としてわざと""(空文字列)を設定しています。
そして、その後ifもしくはelse内でそれぞれの引数の値に上書きしています。
宣言済みの変数の値を上書きする場合は、:=ではなく=を使います。
つまり、:=を使うのは初回(変数宣言時)のみで、2回目以降に値を設定する場合は=になります。
改めて前回作成したShortCodeを見てみる
ここまでの内容を踏まえて、前回作成したShortCodeを改めて見てみましょう。
{{- $rb := cond .IsNamedParams (.Get "rb") (.Get 0) -}}
{{- $rt := cond .IsNamedParams (.Get "rt") (.Get 1) -}}
<ruby>{{- $rb -}}<rp>(</rp><rt>{{- $rt -}}</rt><rp>)</rp></ruby>
まず、1行目、2行目でif文ではなく、cond関数を使っています。
これにより、
- 変数の値の上書きが不要になる(:=のみで済むので記載が簡潔になる)
- そのため行数が減る
といったメリットがありそうです。
また、IsNamedParamsの結果によって変わるのは、
- それぞれの変数の初期値の取得先(名前付きパラメーターが否か)
だけであり、どちらにしても出力するHTMLのタグの構造自体は変わらないことがキモになります。
今回の例では、名前付きパラメーターか否かによらず、同じ出力をするという性質ですので、if文を使わずにcondを使って書いた方が簡潔になると考えられそうです。
(.IsNamedParamsは複数回登場してしまいますが、if文にして変数を上書きするよりはシンプルに書けます)
逆に言えば、条件を満たすか否かで出力するHTMLの構造が大きく変わるような場合は、condではなく、if文を使って書いた方が理解しやすいでしょう。
if文関連で重要なこと
最後に、ここまでの内容を踏まえてまとめると
- 可能なら変数の値の上書きは避け、初期値から変更しないようにする
- 「;=」と「=の」使い分けが必要になるため
- 単純に条件により設定値を切り替えたければcond関数も視野に入れる
- ifやelse内で宣言した変数はその中でしか使えない
- スコープ(有効範囲) の意識が必要
- どうしても上書きする必要のある変数があるなら冒頭で宣言しておく
- 事前に使用する変数を一式宣言してから処理を記述する
- if文とcondの使い分け
- if文は処理の切り分け、condは値の切り分けといったイメージでいると良い
といったことがポイントになります。