🐹

text/templateのgodocのDocumentation

2023/07/24に公開

訳者序文

このドキュメントは正式な日本語訳ではありません。
オリジナルと同様、Creative Commons Attribution 4.0 License の下でライセンスされます。

正式なドキュメントを必要としている方は、以下を参照ください。

https://pkg.go.dev/text/template#section-documentation

Documentation

Overview

template パッケージは、テキスト出力を生成するためのデータ駆動型のテンプレートを実装しています。

HTML 出力を生成するには、html/template パッケージを参照してください。html/template パッケージは、text/template パッケージと同じインターフェイスを備えていますが、特定の攻撃に対して HTML 出力を自動的に保護します。

テンプレートは、データ構造に適用することによって実行されます。テンプレート内のアノテーションは、データ構造の要素 (通常は構造体のフィールドまたはマップのキー) を参照して、実行を制御し、表示する値を導出します。テンプレートを実行すると、構造をたどり、ピリオド「.」で表されるカーソルを設定します。これは「ドット」と呼ばれ、実行に合わせた、データ構造内の現在の位置の値になります。

テンプレートの入力テキストは、任意の形式の UTF-8 エンコードされたテキストです。「Actions」(データ評価または制御構造)は「{{」と「}}」で区切られます。 アクションの外部のすべてのテキストは、変更されずに出力にコピーされます。

解析が完了すると、テンプレートは安全に並列実行できますが、並列実行で Writer を共有すると、出力がインターリーブされる可能性があります。

以下は、「17 items are made of wool」と印刷する簡単な例です。

type Inventory struct {
    Material string
    Count    uint
}
sweaters := Inventory{"wool", 17}
tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
if err != nil { panic(err) }
err = tmpl.Execute(os.Stdout, sweaters)
if err != nil { panic(err) }

より複雑な例を示していきます。

Text and spaces

デフォルトでは、テンプレートの実行時に、アクション間のすべてのテキストがそのままコピーされます。たとえば、上の例の文字列「 items are made of 」は、プログラムの実行時に標準出力に表示されます。

ただし、テンプレートのソースコードの書式設定を容易にするために、アクションの左区切り文字 (デフォルトでは「{{」) の直後にマイナス記号と空白が続く場合、直前のテキストから末尾の空白がすべて削除されます。同様に、右区切り文字 (「}}」) の前に空白とマイナス記号がある場合、先頭の空白はすべて直後のテキストから削除されます。これらのトリムマーカーには、空白が存在する必要があります。「{{- 3}}」は「{{3}}」と似ていますが、直前のテキストが削除され、「{{-3}}」は数値 -3 を含むアクションとして解析されます。

たとえば、次のソースコードのテンプレートを実行する場合、

"{{23 -}} < {{- 45}}"

生成される出力は次のようになります

"23<45"

このトリミングでは、空白文字の定義は Go と同じです (つまり、スペース、水平タブ、復帰、改行の各文字です)。

Actions

以下がアクションのリストです。「arguments」と「pipelines」はデータの評価であり、以下の対応する各セクションで詳細に定義されます。

{{/* a comment */}}
{{- /* a comment with white space trimmed from preceding and following text */ -}}

コメント。無視されます。改行が含まれる場合があります。コメントはネストできず、ここに示すように区切り文字で開始および終了する必要があります。

{{pipeline}}

pipeline の値のデフォルトのテキスト表現 (fmt.Print によって出力されるものと同じ) が出力にコピーされます。

{{if pipeline}} T1 {{end}}

pipeline の値が空の場合、出力は生成されません。 それ以外の場合は、T1 が実行されます。 空の値とは、false、0、任意の nil ポインターまたはインターフェース値、および長さ 0 の任意の配列、スライス、マップ、または文字列です。
「ドット」は影響を受けません。

{{if pipeline}} T1 {{else}} T0 {{end}}

pipeline の値が空の場合、T0 が実行されます。 それ以外の場合は、T1 が実行されます。 「ドット」は影響を受けません。

{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}

if-else チェーンの外観を簡素化するために、if の else アクションには別の if を直接含めることができます。 効果は {{if Pipeline}} T1 {{else}}{{if Pipeline}} T0 {{end}}{{end}} と書くのと全く同じです。

{{range pipeline}} T1 {{end}}

pipeline の値は、配列、スライス、マップ、またはチャネルでなければいけません。pipeline の値の長さがゼロの場合は、何も出力されません。それ以外の場合は、配列、スライス、またはマップの次の要素が「ドット」に設定され、T1 が実行されます。 pipeline の値がマップで、キーが定義された順序を持つ基本タイプである場合、要素はソートされたキーの順序でアクセスされます。

{{range pipeline}} T1 {{else}} T0 {{end}}

pipeline の値は、配列、スライス、マップ、またはチャネルでなければいけません。pipeline の値の長さがゼロの場合、「ドット」は影響を受けず、T0 が実行されます。それ以外の場合は、配列、スライス、またはマップの次の要素が「ドット」に設定され、T1 が実行されます。

{{break}}

最も内側の {{range pipeline}} ループは早期に終了し、現在の反復を停止し、残りのすべての反復を省略します。

{{continue}}

最も内側の {{range pipeline}} ループの現在の反復が停止され、ループは次の反復を開始します。

{{template "name"}}

指定された "name" のテンプレートが nil データで実行されます。

{{template "name" pipeline}}

指定された "name" のテンプレートが、pipeline の値が設定された「ドット」で実行されます。

{{block "name" pipeline}} T1 {{end}}

block は、テンプレート {{define "name"}} T1 {{end}} を定義し、それを {{template "name" pipeline}} で実行するための省略表現です。 一般的な用途は、ルートテンプレートのセットを定義し、その中の block テンプレートを再定義してカスタマイズします。

{{with pipeline}} T1 {{end}}

pipeline の値が空の場合、出力は生成されません。 それ以外の場合、「ドット」は pipeline の値に設定され、T1 が実行されます。

{{with pipeline}} T1 {{else}} T0 {{end}}

pipeline の値が空の場合、「ドット」は影響を受けず、T0 が実行されます。 それ以外の場合、「ドット」は pipeline の値に設定され、T1 が実行されます。

Arguments

argument は単純な値であり、次のいずれかで示されます。

  • Go の構文のブール定数、文字列定数、文字定数、整数定数、浮動小数点定数、虚数定数、または複素数定数。 これらは Go の型なし定数のように動作します。 Go と同様、大きな整数定数が関数に割り当てられるとき、または関数に渡されるときにオーバーフローするかどうかは、ホスト マシンの int が 32 ビットであるか 64 ビットであるかによって決まることに注意してください。
  • キーワード nil は、型なしの Go nil を表します。
  • '.' (ピリオド)文字: . 結果は「ドット」の値です。
  • variable 名。$piOver2$ など、ドル記号が前に付いた (空の可能性がある) 英数字文字列です。結果は variable の値です。variable については以下で説明します。
  • データのフィールドの名前。データは構造体でなければならず、.Field のように、先頭にピリオドを付けます。結果はフィールドの値です。フィールド呼び出しは連鎖することができます: .Field1.Field2 フィールドは連鎖を含めて変数に対して評価することもできます: $x.Field1.Field2
  • データのキーの名前。データはマップでなければならず、.Key のように、先頭にピリオドを付けます。結果は、キーによってインデックス付けされたマップ要素の値です。キーの呼び出しは、任意の深さまでチェーンしてフィールドと組み合わせることができます: .Field1.Key1.Field2.Key2 キーは英数字の識別子である必要がありますが、フィールド名とは異なり、大文字で始める必要はありません。キーは、連鎖を含む変数に対して評価することもできます: $x.key1.key2
  • .Method のように、データの引数なしメソッドの名前。先頭にピリオドが付きます。結果は、レシーバーとしてドットを使用したメソッド dot.Method() を呼び出した値になります。このようなメソッドには、(任意の型の) 1つの戻り値、または2つの戻り値の2番目の戻り値がエラーである必要があります。戻り値が2つあり、返されたエラーが非 nil の場合、実行は終了し、エラーが Execute の値として呼び出し元に返されます。メソッド呼び出しは、任意の深さまで連鎖させてフィールドやキーと組み合わせることができます: .Field1.Key1.Method1.Field2.Key2.Method2 メソッドは連鎖を含めて変数に対して評価することもできます: $x.Method1.Field
  • fun などの引数なし function の名前。結果は関数 fun() を呼び出した値です。 戻り値の型と値はメソッドと同様に動作します。 function と function の名前は以下で記述します。
  • グループ化するための、上記のいずれかのインスタンスが括弧で囲まれたもの。 結果は、フィールドまたはマップキーの呼び出しによってアクセスされます。 print (.F1 arg1) (.F2 arg2) (.StructValuedMethod "arg").Field

引数は任意の型に評価できます。 ポインターの場合、実装は必要に応じて自動的に基本型として扱います。 構造体の関数オブジェクトのフィールドなど、評価によって関数が得られた場合、その関数は自動的に呼び出されませんが、if アクションなどの真理値として使用できます。 これを呼び出すには、以下で定義されている call function を使用します。

Pipelines

パイプラインは、「コマンド」で、連鎖したものの場合もあります。「コマンド」は単純な値 (argument)、または関数やメソッド呼び出しで、複数の argument を持つこともあります。

  • Argument
    • 結果は argument を評価した値です。
  • .Method [Argument...]
    • メソッドは単独でもチェーンの最後の要素でも構いませんが、チェーンの途中にあるメソッドとは異なり、引数を取ることができます。 結果は、引数を指定してメソッドを呼び出した値です: dot.Method(Argument1, etc.)
  • functionName [Argument...]
    • 結果は、名前に関連付けられた関数を呼び出した値です: function(Argument1, etc.) function と function の名前については以下で説明します。

パイプライン文字「|」で一連のコマンドを区切ることにより、パイプラインを「連鎖」させることができます。 連鎖されたパイプラインでは、各コマンドの結果が次のコマンドの最後の引数として渡されます。 パイプラインの最後のコマンドの出力は、パイプラインの値です。

コマンドの出力は1つの値または2つの値で、2番目の値はエラー型です。 2番目の値が存在し、非 nil と評価された場合、実行は終了し、Execute の呼び出し元にエラーが返されます。

Variables

アクション内のパイプラインは、結果を取得するために変数を初期化することがあります。 この初期化の構文は以下のとおりです。

$variable := pipeline

$variable は変数の名前です。 変数を宣言するアクションでは出力は生成しません。

既に宣言された変数に、次の構文を使用して値を割り当てることもできます。

$variable = pipeline

「range」アクションが変数を初期化する場合、その変数は反復の連続する要素に設定されます。 また、「range」では、カンマで区切られた2つの変数を宣言できます。

range $index, $element := pipeline

この場合、$index と $element は、それぞれ配列/スライスの index またはマップのキーと element の連続する値が設定されます。変数が1つだけの場合は、その変数に element が割り当てられることに注意してください。これは、Go の range 句の規則とは逆です。

変数のスコープは、変数が宣言されている制御構造 (「if」、「with」、または「range」) の「end」アクションまで、またはそのような制御構造がない場合はテンプレートの最後まで拡張されます。テンプレートの呼び出しは、その呼び出し時点からの変数を継承しません。

実行が開始されると、$ は Execute に渡されたデータ引数が設定されます。つまり「ドット」の最初の値として設定されます。

Examples

以下に、pipelinevariable を示す1行テンプレートの例をいくつか示します。いずれも引用符で囲まれた単語「"output"」を生成します。

{{"\"output\""}}

文字列定数。

{{`"output"`}}

raw 文字列定数。

{{printf "%q" "output"}}

関数呼び出し

{{"output" | printf "%q"}}

前のコマンドから最後の引数が取得される関数呼び出し。

{{printf "%q" (print "out" "put")}}

括弧で囲まれた引数。

{{"put" | printf "%s%s" "out" | printf "%q"}}

より複雑な呼び出し。

{{"output" | printf "%s" | printf "%q"}}

長い連鎖。

{{with "output"}}{{printf "%q" .}}{{end}}

with アクションで「ドット」を使う。

{{with $x := "output" | printf "%q"}}{{$x}}{{end}}

with アクションで変数を作って、使う。

{{with $x := "output"}}{{printf "%q" $x}}{{end}}

with アクションで作った変数を他のアクションで使う。

{{with $x := "output"}}{{$x | printf "%q"}}{{end}}

同じだが、パイプライン。

Functions

実行中、2つの関数マップから関数を探します。最初はテンプレート内、次にグローバル関数です。 デフォルトでは、テンプレートに関数は定義されていませんが、Funcs メソッドを使用して関数を追加できます。

事前定義されたグローバル関数の名前は次のとおりです。

  • and
    • 最初の空の引数または最後の引数を返すことにより、引数の論理値の AND を返します。 つまり、and x yif x then y else x として動作します。 評価は引数を左から右に進め、結果が決定されると戻ります。
  • call
    • 最初の引数 (関数である必要があります) に対して、残りの引数をパラメーターとして呼び出した結果を返します。 したがって、call .X.Y 1 2 は、Go 表記では dot.X.Y(1, 2) になります。ここで、Y は関数値フィールド、マップエントリなどです。最初の引数は、評価の結果、(print などの事前定義された関数とは異なる) 関数タイプの値を生成する必要があります。 関数は1つまたは2つの結果値を返す必要があり、2番目の結果値はエラー型です。 引数が関数と一致しない場合、または返されたエラー値が非 nil の場合、実行は停止します。
  • html
    • 引数のテキスト表現に相当するエスケープされた HTML を返します。 この関数は、いくつかの例外を除いて、html/template では使用できません。
  • index
    • 最初の引数を次の引数でインデックス付けした結果を返します。 したがって、index x 1 2 3 は、Go 構文では x[1][2][3] となります。 インデックス付きの各項目は、マップ、スライス、または配列でなければなりません。
  • slice
    • slice は、最初の引数を残りの引数でスライスした結果を返します。 したがって、Go 構文では、slice x 1 2x[1:2] ですが、slice xx[:]slice x 1x[1:]slice x 1 2 3x[1:2:3] です。最初の引数は文字列、スライス、または配列でなければなりません。
  • js
    • 引数のテキスト表現と同等のエスケープされた JavaScript を返します。
  • len
    • 引数の長さを整数で返します。
  • not
    • 単一の引数の論理値の否定を返します。
  • or
    • 最初の空でない引数または最後の引数を返すことによって、引数の論理値の OR を返します。つまり、or x yif x then x else y として動作します。 評価は引数を左から右に進め、結果が決定されると戻ります。
  • print
    • fmt.Sprint の別名
  • printf
    • fmt.Sprintf の別名
  • println
    • fmt.Sprintln の別名
  • urlquery
    • 引数のテキスト表現のエスケープされた値を、URL クエリへの埋め込みに適した形式で返します。 この機能は、いくつかの例外を除いて、html/template では使用できません。

論理値関数は、ゼロ値を false として取り、ゼロ以外の値を true として取ります。

関数として定義された一連の2項比較演算子もあります。

  • eq
    • arg1 == arg2 の真偽値を返す
  • ne
    • arg1 != arg2 の真偽値を返す
  • lt
    • arg1 < arg2 の真偽値を返す
  • le
    • arg1 <= arg2 の真偽値を返す
  • gt
    • arg1 > arg2 の真偽値を返す
  • ge
    • arg1 >= arg2 の真偽値を返す

多方向の等価テストを単純にするため、eq(だけ)は2つ以上の引数を受け入れ、2番目以降を最初の引数と比較して、以下のようになります。

arg1==arg2 || arg1==arg3 || arg1==arg4 ...

(ただし、Go の || とは異なり、eq は関数呼び出しであり、すべての引数が評価されます。)

比較関数は、Go が比較可能なものとして定義した型の値に対して機能します。 整数などの基本型の場合、ルールは緩和されます。サイズと正確な型は無視されるため、符号付きまたは符号なしの任意の整数値を他の整数値と比較できます。 (ビット パターンではなく算術値が比較されるため、すべての負の整数はすべての符号なし整数よりも小さくなります。) ただし、通常どおり、int と float32 などを比較することはできません。

Associated templates

各テンプレートには、作成時に指定された文字列によって名前が付けられます。また、各テンプレートは、名前によって呼び出すことができる0個以上の他のテンプレートに関連付けられています。このような関連付けは推移的であり、テンプレートの名前空間を形成します。

テンプレートは、テンプレート呼び出しを使用して、関連付けられた別のテンプレートをインスタンス化する場合があります。上記の template アクションの説明を参照してください。この名前は、呼び出しをするテンプレート内で関連付けられたテンプレートの名前である必要があります。

Nested template definitions

テンプレートを解析するときに、別のテンプレートを定義し、解析中のテンプレートに関連付けることができます。テンプレート定義は、Go プログラムのグローバル変数と同様に、テンプレートの最上位に存在する必要があります。

このような定義の構文では、各テンプレート宣言を define アクションと end アクションで囲みます。

define アクションでは、文字列定数で、作成されるテンプレートに名前を付けます。 簡単な例を次に示します。

{{define "T1"}}ONE{{end}}
{{define "T2"}}TWO{{end}}
{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
{{template "T3"}}

これにより、2つのテンプレート T1 と T2 が定義され、実行時に他の2つを呼び出す3番目の T3 が定義されます。最後に、T3 を呼び出します。このテンプレートを実行すると、以下のテキストが生成されます。

ONE TWO

構造上、テンプレートは1つの関連付けにのみ所属できます。複数の関連付けからテンプレートをアドレス指定できるようにする必要がある場合は、テンプレート定義を複数回解析して個別の *Template 値を作成するか、Clone メソッドまたは AddParseTree メソッドを使用してコピーする必要があります。

Parse は、関連するさまざまなテンプレートをアセンブルするために複数回呼び出される場合があります。ファイルに保存されている関連テンプレートを解析する簡単な方法については、ParseFiles および ParseGlob の関数とメソッドを参照してください。

テンプレートは直接実行することも、名前で識別される関連テンプレートを実行する ExecuteTemplate を通じて実行することもできます。 上記の例を呼び出すには、次のように書くことができます。

err := tmpl.Execute(os.Stdout, "no data needed")
if err != nil {
    log.Fatalf("execution failed: %s", err)
}

また、特定のテンプレートを名前で明示的に呼び出すには、以下のようにします。

err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed")
if err != nil {
    log.Fatalf("execution failed: %s", err)
}
GitHubで編集を提案
株式会社ROBONの技術ブログ

Discussion