ããã¯ããªã«ãããããŠæžãããã®ïŒ
Goã®ãã³ãã¬ãŒããšã³ãžã³ã調ã¹ãŠã¿ããããªãšæã£ãã®ã§ãããæšæºã©ã€ãã©ãªã«ãããããªã®ã§ããã¡ãã詊ããŠ
ã¿ãããšã«ããŸããã
Goã®æšæºã©ã€ãã©ãªã«ãããã³ãã¬ãŒããšã³ãžã³
text/templateãšãhtml/templateã®2çš®é¡ãããããã§ãã
template - The Go Programming Language
template - The Go Programming Language
ååããæ³åã¯ã€ããŸãããtext/templateã¯ããã¹ãçæã®ããã®ãã³ãã¬ãŒããšã³ãžã³ã§ãhtml/templateã¯HTMLçæã®ããã®
ãã³ãã¬ãŒããšã³ãžã³ã§ãã
HTMLåãã®ãšã¹ã±ãŒããªã©ãå¹ããããªã®ã§ãHTMLçæã«äœ¿ãããæã¯html/templateã䜿ããŸãããã
ä»åã¯text/templateã䜿ããŸãã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã
$ go version go version go1.15.6 linux/amd64
ã¢ãžã¥ãŒã«ã®åæåã
$ go mod init text-template-example go: creating new go.mod: module text-template-example
åäœç¢ºèªã«ã¯ãtestifyã䜿ãããšã«ããŸãã
go.mod
module text-template-example go 1.15 require github.com/stretchr/testify v1.6.1
ãã¹ãã³ãŒãã®é圢
åäœç¢ºèªã¯ããã¹ãã³ãŒãã§è¡ã£ãŠãããŸããé圢ã¯ããããªæãã§çšæã
template_test.go
package main import ( "fmt" "strings" "testing" "text/template" "github.com/stretchr/testify/assert" ) // ããã«ãã¹ããæžãïŒïŒ
text/templateã䜿ã
ã§ã¯ãtext/templateã䜿ã£ãŠã¿ãŸãããã
template - The Go Programming Language
äœ¿ãæ¹ã®åºæ¬çãªã€ã¡ãŒãžã¯Overviewã«æžããŠãããŸãã
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) }
ãã³ãã¬ãŒãã¯UTF-8ã§äœæãããä»»æã®æååã§ã"ã¢ã¯ã·ã§ã³"ãšåŒã°ããããŒã¿ã®è©äŸ¡ãå¶åŸ¡æ§é ã«äœ¿ãããæ§æã¯{{ãš}}ã§
å²ã£ãŠè¡šçŸãããŸãã
æŒãããŠãããæ¹ããããããªèŠçŽ ã¯ä»¥äžãªã®ã§ããããã©ãå
šéšã¯äœ¿ããªãã®ã§ã¡ãã£ãšãã€ããã€ãŸãã§äœ¿ã£ãŠããããšã«
ããŸãããã
ãŸãã¯ãããªæãã§ã
func TestTextTemplateGettingStarted(t *testing.T) { type Person struct { FirstName string LastName string } person := &Person{FirstName: "ã«ããª", LastName: "磯é"} tmpl, err := template.New("test_template").Parse("{{.FirstName}} {{.LastName}}") if err != nil { t.Fatal(err) } writer := new(strings.Builder) tmpl.Execute(writer, person) assert.Equal(t, "ã«ã㪠磯é", writer.String()) }
Newã§æå®ããååã®ãã³ãã¬ãŒããäœæããŸãã
ãã®æç¹ã§ã¯äžèº«ã¯ãªãããã®åŸã®Parseã§ãã³ãã¬ãŒããæååãšããŠæž¡ããããŒã¹ããŠããŸãã
ãã®ãããã§ããããã³ãã¬ãŒãã®æ§æã誀ã£ãŠãããšãerrorãè¿ã£ãŠããŸãã
tmpl, err := template.New("test_template").Parse("{{.FirstName}} {{.LastName}}") if err != nil { t.Fatal(err) }
ã§ããã³ãã¬ãŒãã«å€ããã€ã³ãããŠè©äŸ¡ããŸãã
writer := new(strings.Builder)
tmpl.Execute(writer, person)
ä»åã¯ãè©äŸ¡çµæã¯æååãšããŠååŸããŸããã
æåŸã«ç¢ºèªã
assert.Equal(t, "ã«ã㪠磯é", writer.String())
æ§é äœã®å€ãåç §ããå Žåãã¡ã³ããŒã®ååã¯å€§æåã§å§ããå¿ èŠããããŸãã
ããã§ããã
Although the key must be an alphanumeric identifier, unlike with field names they do not need to start with an upper case letter.
å°æåã«ãããšããã³ãã¬ãŒãã®è©äŸ¡æã«åç §ã§ããã«ãšã©ãŒã«ãªããŸãã
func TestTextTemplateGettingStartedFailed(t *testing.T) { type Person struct { firstName string lastName string } person := &Person{firstName: "ã«ããª", lastName: "磯é"} tmpl, _ := template.New("test_template").Parse("{{.firstName}} {{.fastName}}") writer := new(strings.Builder) err := tmpl.Execute(writer, person) assert.Contains(t, err.Error(), "executing \"test_template\" at <.firstName>: firstName is an unexported field of struct type *main.Person") }
ãã®ããã«ãã³ãã¬ãŒãã®è©äŸ¡ã«å€±æããå Žåã¯ãExecuteãerrorãè¿ããŠããŸãã
ãã³ãã¬ãŒãããã¡ã€ã«ãšããŠçšæãã
ãã³ãã¬ãŒãã¯ãã¡ã€ã«ãšããŠãçšæã§ããŸãããã®å ŽåãParseFilesã䜿ããŸãã
func TestTextTemplateFromFile(t *testing.T) { type Person struct { FirstName string LastName string } person := &Person{FirstName: "ã«ããª", LastName: "磯é"} tmpl, _ := template.New("person.tmpl").ParseFiles("testdata/person.tmpl") writer := new(strings.Builder) tmpl.Execute(writer, person) assert.Equal(t, "ã«ã㪠磯é", writer.String()) }
ãã³ãã¬ãŒãã®ååã¯ãããã¡ã€ã«åãã«ãªãããã§ããNewã§æå®ããŠããéšåã¯ããã¡ã€ã«åãšåãããæ¹ãããããã§ãã
Since the templates created by ParseFiles are named by the base names of the argument files, t should usually have the name of one of the (base) names of the files. If it does not, depending on t's contents before calling ParseFiles, t.Execute may fail.
ä»åã®ãã³ãã¬ãŒããã¡ã€ã«ã¯ãå ã»ã©æååã§çšæãããã®ãšåãå 容ã«ããŠãããŸãã testdata/person.tmpl
{{.FirstName}} {{.LastName}}
ãŸãããã¡ã€ã«ã®å Žåã¯Newã䜿ããã«ãããªãParseFilesããããšãã§ããŸãã
func TestTextTemplateFromFileSimply(t *testing.T) { type Person struct { FirstName string LastName string } person := &Person{FirstName: "ã«ããª", LastName: "磯é"} tmpl, _ := template.ParseFiles("testdata/person.tmpl") writer := new(strings.Builder) tmpl.Execute(writer, person) assert.Equal(t, "ã«ã㪠磯é", writer.String()) }
ãã¡ãã䜿ã£ãŠããŸãã
ã©ã¡ãã®é¢æ°ã«ãèšããã®ã§ãããè€æ°ã®ãã¡ã€ã«ãããŒã¹ã§ããããã§ãããã®æåã¯ä»åã¯ç¢ºèªããŸããã§ããâŠã
ãã以éã¯ããã³ãã¬ãŒããæååã§å®çŸ©ããŠäœ¿ãããšã«ããŸãã
.ã¯çŸåšã®äœçœ®ã«å¿ããå€ãæã
.ã¯ãçŸåšã®äœçœ®ã«å¿ããå€ãæããŸãã
Execution of the template walks the structure and sets the cursor, represented by a period '.' and called "dot", to the value at the current location in the structure as execution proceeds.
ãšæžããšããããããªãæããªã®ã§ããããããã¬ãã«ã§äœ¿ããšãã³ãã¬ãŒãã«ãã€ã³ããããå€ãã®ãã®ãæããŸãã
func TestDot(t *testing.T) { tmpl, _ := template.New("test_template").Parse("Hello {{.}}!!") writer := new(strings.Builder) tmpl.Execute(writer, "World") assert.Equal(t, "Hello World!!", writer.String()) }
ä»åã®æå®ã¯ããã¡ãã
Hello {{.}}!!
ã«ãŒãïŒåŸè¿°ïŒã§äœ¿ã£ãããããšã.ãæãå€ãå€ãã£ãŠãããŸãã
ãªã®ã§ã.èªäœã®èª¬æã¯ããã¥ã¡ã³ãäžã¯ãã®ãããªè¡šçŸã«ãªã£ãŠããŸãã
The result is the value of dot.
Variables
ãã³ãã¬ãŒãäžã§ã倿°ãå®çŸ©ã§ããŸãã
$ã䜿ã£ãŠè¡šçŸããŸããå®£èšæã¯:=ã§å€ãæå®ãããã§ã«å®£èšãããŠãã倿°ã«å€ãåå²åœãŠããæã¯=ã䜿ããŸãã
éåžžã®å€æ°ãšåãã§ããã
func TestVariable(t *testing.T) { tmpl, _ := template.New("test_template").Parse(` {{$val := .}} Hello {{$val}}!!`) writer := new(strings.Builder) tmpl.Execute(writer, "World") assert.Equal(t, ` Hello World!!`, writer.String()) }
ä»åã¯ã.ã倿°$valã«å²ãåœãŠãŸããã
{{$val := .}}
ãã¹ããããèŠçŽ ãåç §ãã
æ§é äœå ã®ãã¹ããããèŠçŽ ïŒæ§é äœã®ã¡ã³ããŒïŒããåç §ã§ããŸãã
func TestNestedStruct(t *testing.T) { type Inner struct { InnerField string } type Outer struct { OuterField string Inner *Inner } inner := &Inner{InnerField: "Bar"} outer := &Outer{OuterField: "Foo", Inner: inner} tmpl, _ := template.New("test_template").Parse("{{.OuterField}} {{.Inner.InnerField}}") writer := new(strings.Builder) tmpl.Execute(writer, outer) assert.Equal(t, "Foo Bar", writer.String()) }
ãã®éšåã§ããã
{{.Inner.InnerField}}
ããããã¹ã©ã€ã¹ãæ±ã
ãã³ãã¬ãŒãäžã§ãããããã¹ã©ã€ã¹ãé åãæ±ãããšãã§ããŸãã
ãŸãã¯ãããã
func TestMap(t *testing.T) { m := map[string]int{ "key1": 1, "key2": 2, "key3": 3, } tmpl, _ := template.New("test_template").Parse(` key1 = {{.key1}} key2 = {{.key2}} key3 = {{.key3}} ---------------------------------------- {{range . -}} {{.}} {{end -}} ---------------------------------------- {{range $key, $value := . -}} {{$key}} = {{$value}} {{end -}} `) writer := new(strings.Builder) tmpl.Execute(writer, m) assert.Equal(t, ` key1 = 1 key2 = 2 key3 = 3 ---------------------------------------- 1 2 3 ---------------------------------------- key1 = 1 key2 = 2 key3 = 3 `, writer.String()) }
.ããŒã§å€ãåç
§ã§ããŸãã
key1 = {{.key1}}
key2 = {{.key2}}
key3 = {{.key3}}
rangeïŒã«ãŒãïŒã䜿ããšãå€ã.ã«ãã€ã³ããããŸãã
{{range . -}}
{{.}}
{{end -}}
rangeã§ãããŒãšå€ãåãåºãããšãå¯èœã§ãã
{{range $key, $value := . -}}
{{$key}} = {{$value}}
{{end -}}
ã¹ã©ã€ã¹ã
func TestSlice(t *testing.T) { values := []string{"value1", "value2", "value3"} tmpl, _ := template.New("test_template").Parse(` {{$vals := .}} {{index $vals 0}} {{index $vals 1}} {{index $vals 2}} ---------------------------------------- {{range $vals -}} {{.}} {{end -}} ---------------------------------------- {{range $index, $element := $vals -}} {{$index}} = {{$element}} {{end -}} `) writer := new(strings.Builder) tmpl.Execute(writer, values) assert.Equal(t, ` value1 value2 value3 ---------------------------------------- value1 value2 value3 ---------------------------------------- 0 = value1 1 = value2 2 = value3 `, writer.String()) }
ã¡ãã£ãšèŠã¥ããã£ãã®ã§ã.ã倿°ã«ãã€ã³ãããŸããã
{{$vals := .}}
ã¹ã©ã€ã¹ã®èŠçŽ ããã€ã³ããã¯ã¹ãæå®ããŠåç
§ããã«ã¯index颿°ã䜿ããŸãã
{{index $vals 0}}
{{index $vals 1}}
{{index $vals 2}}
rangeã䜿ã£ãå Žåã¯ãèŠçŽ ãã®ãã®ããããã¯ã€ã³ããã¯ã¹ãšèŠçŽ ããã€ã³ãã§ããŸãã
{{range $vals -}}
{{.}}
{{end -}}
----------------------------------------
{{range $index, $element := $vals -}}
{{$index}} = {{$element}}
{{end -}}
- ã§ãã¯ã€ã¹ããŒã¹ãåé€ãã
ãã£ããããã³ãã¬ãŒãã«æã
{{ -}}ã¿ãããªã®ãç»å Žããã®ã§ãããããã¯ãã¯ã€ãã¹ããŒã¹ãåé€ãã广ããããŸãã
{{- }}ã§ããã°æå®ããã¢ã¯ã·ã§ã³ããå·Šã®ãã¯ã€ãã¹ããŒã¹ã{{ -}}ã§ããã°æå®ããã¢ã¯ã·ã§ã³ããå³ã®ãã¯ã€ãã¹ããŒã¹ã
åé€ããŸããå·Šå³äž¡æ¹æå®ããŠãOKã§ãã
ãŸããããã§ãã"ãã¯ã€ãã¹ããŒã¹"ãšããã®ã¯Goãšåãã§ãã¹ããŒã¹ãæ°Žå¹³ã¿ããCRãLFã§ãã
ããšãã°ããããªæãã«ã¹ããŒã¹ãæ¹è¡ã倧éã«å
¥ã£ããã³ãã¬ãŒãã䜿ã£ãŠã-ãšçµã¿åãããããšã§äžæ°ã«åé€ãããŸãã
func TestTrimWhiteSpaceBetweenAction(t *testing.T) { m := map[string]string{ "word1": "Hello", "word2": "World", } tmpl, _ := template.New("test_template").Parse(`| {{- .word1}} | | {{ .word2 -}} | {{ .word1 -}} {{- .word2}}`) writer := new(strings.Builder) tmpl.Execute(writer, m) assert.Equal(t, "|Hello | | World| HelloWorld", writer.String()) }
ãŸããåé€ããããã¯ã€ãã¹ããŒã¹ã¯ããããŸã§{{-ã-}}ã®å·Šå³ã§ããããã³ãã¬ãŒãã«åã蟌ãã å€ã®ãã¯ã€ãã¹ããŒã¹ã
åé€ãããããã§ã¯ãããŸããã
func TestTrimWhiteSpaceBetweenAction2(t *testing.T) { m := map[string]string{ "word1": " Hello ", "word2": " World ", } tmpl, _ := template.New("test_template").Parse(`{{ .word1 -}}{{- .word2}}`) writer := new(strings.Builder) tmpl.Execute(writer, m) assert.Equal(t, " Hello World ", writer.String()) }
æ¡ä»¶åå²
ifãelse ifãelseã䜿ã£ãæ¡ä»¶åå²ãæžããŸãã
andãorã䜿ãããšãã§ããŸãã
func TestBoolConditional(t *testing.T) { type Sample struct { Flag1 bool Flag2 bool Flag3 bool Message string } s := &Sample{Flag1: true, Flag2: true, Flag3: false, Message: "Hello World!!"} tmpl, _ := template.New("test_template").Parse(` {{if .Flag1 -}} Message = "{{.Message}}" {{end}} {{if .Flag3 -}} Message = {{.Message}} {{else -}} Flag3 is false {{end}} {{if .Flag3 -}} Message = {{.Message}} {{else if .Flag2 -}} Flag2 is true {{end}} {{if and .Flag1 .Flag2 -}} Flag1 and Flag2 are true {{end}} {{if and .Flag1 .Flag3 -}} Flag1 and Flag3 are true {{else -}} Either Flag1 or Flag3 is false {{end}} {{if or .Flag1 .Flag3 -}} Either Flag1 or Flag3 is true {{end -}} `) writer := new(strings.Builder) tmpl.Execute(writer, s) assert.Equal(t, ` Message = "Hello World!!" Flag3 is false Flag2 is true Flag1 and Flag2 are true Either Flag1 or Flag3 is false Either Flag1 or Flag3 is true `, writer.String()) }
ã¡ãœããåŒã³åºããè¡ã
ã¡ãœããåŒã³åºããå¯èœã§ãã
åŒæ°ã®æå®ã¯ã()ã䜿ããªãããšã«æ³šæããŸãããã
type Owner struct { name string } func (o *Owner) SayHello(s string) string { return fmt.Sprintf("Hello %s%s", o.name, s) } func TestMethodCall(t *testing.T) { o := &Owner{name: "ã«ããª"} tmpl, _ := template.New("test_template").Parse(` Method Call = {{.SayHello "!!"}} `) writer := new(strings.Builder) tmpl.Execute(writer, o) assert.Equal(t, ` Method Call = Hello ã«ããª!! `, writer.String()) }
ãŸãšã
Goæšæºã®ãã³ãã¬ãŒããšã³ãžã³ã®ãã¡ãtext/templateã詊ããŠã¿ãŸããã
æåã¯äœ¿ãæ¹ãããããããªãã£ãã®ã§ãããåãããã調ã¹ããããŠãããã¡ã«ãã ãã¶é°å²æ°ã¯ãããããã«ãªã£ã
æ°ãããŸãã
ä»åãå šéšã®æ©èœãæ§æãç¶²çŸ ããããã§ã¯ãªãã§ãããããšã¯ããã¥ã¡ã³ããèŠãªããé²ããŠãããããªããšã