Tekstgenerering med Go Maler

Oversikt

Tekst er rundt oss som programvareutviklere. Koden er tekst, HTML er tekst, XNL / JSON / YAML / TOML er tekst, Markdown er tekst, CSV er tekst. Alle disse tekstformatene er designet for å imøtekomme både mennesker og maskiner. Mennesker skal kunne lese og redigere tekstformater med enkle tekstredigerere. 

Men det er mange tilfeller der du må generere tekst i et bestemt format. Du kan konvertere fra ett format til et annet, lage din egen DSL, generere litt hjelpekode automatisk, eller bare tilpasse en epost med brukerspesifikke opplysninger. Uansett hva behovet er, er Go mer enn å kunne hjelpe deg på veien med sine kraftige maler. 

I denne opplæringen lærer du om innlegg og utgaver av Go-maler og hvordan du bruker dem til kraftig tekstgenerering.

Hva er Go Maler?

Go maler er objekter som administrerer litt tekst med spesielle plassholdere som kalles handlinger, som er omsluttet av dobbeltkrøllede braces: noen handling. Når du utfører malen, gir du den en Go-struktur som har dataene som plassholderne trenger. 

Her er et raskt eksempel som genererer knock knock vitser. En knock knock joke har et veldig strengt format. De eneste tingene som endrer er identiteten til knockeren og slaglinjen.

pakke viktigste import ("tekst / mal" "os") type Joke struct Hvem streng Punchline streng func main () t: = template.New ("Knock Knock Joke") tekst: = 'Knock Knock \ nWho er der? .Who .Who hvem? .Punchline 't.Parse (tekst) vitser: = [] Joke "Etch", "Bless you!", "Cow goes", "No, cow goes moo!", For _, spøk: = utvalg vitser t.Execute (os.Stdout, joke) Output: Knock Knock Hvem er det? Etch Etch hvem? Velsigne deg! Knock Knock Hvem er det? Kua går Kua går hvem? Nei, ku går bra!

Forstå malhandlinger

Malen syntaxen er veldig kraftig, og den støtter handlinger som data accessors, funksjoner, rørledninger, variabler, conditionals og looper.

Data Accessors

Data accessors er veldig enkle. De drar bare data ut av strukturen som starter. De kan også bore inn i nestede strukturer også:

func main () familie: = Familie Far: Person "Tarzan", Mor: Person "Jane", ChildrenCount: 2, t: = template.New ("Father") navnet er .Father.Name "t.Parse (tekst) t.Execute (os.Stdout, familie) 

Hvis dataene ikke er en struktur, kan du bare bruke . Å få tilgang til verdien direkte:

funk main () t: = template.New ("" t.Parse ("Alt går: . \ n") t.Execute (os.Stdout, 1) t.Execute (os.Stdout, "to") t.Kjøre (os.Stdout, 3.0) t.Kjøre (os.Stdout, map [streng] int "fire": 4) Utgang: Alt går: 1 Alt går: to Alt går: 3 Alt går: kart [fire: 4] 

Vi vil se senere hvordan vi skal håndtere arrays, skiver og kart.

funksjoner

Funksjoner virkelig heve hva du kan gjøre med maler. Det er mange globale funksjoner, og du kan til og med legge til mal-spesifikke funksjoner. Den komplette listen over globale funksjoner er tilgjengelig på Go-nettstedet.

Her er et eksempel på hvordan du bruker printf fungere i en mal:

func main () t: = template.New ("") t.Parse ('Holder bare 2 decimaler av π: printf "% .2f".'). Utfør (os.Stdout, math. Pi) Utgang: Holder bare 2 desimaler av π: 3,14 

rørledninger

Rørledninger lar deg bruke flere funksjoner til gjeldende verdi. Kombinere forskjellige funksjoner utvider måtene du kan skille og telle dine verdier på. 

I den følgende koden kjedede jeg tre funksjoner. For det første utfører anropsfunksjonen funksjonspasset til Henrette(). Og så len funksjonen returnerer lengden på resultatet av inngangsfunksjonen, som er 3 i dette tilfellet. Endelig, den printf funksjonen skriver ut antall elementer.

func main () t: = template.New ("") t.Parse ("call. | len | printf"% d items " ') t.Kjøre (os.Stdout, func returnere "abc") Output: 3 elementer

variabler

Noen ganger vil du gjenbruke resultatet av en kompleks rørledning flere ganger. Med Go-maler kan du definere en variabel og gjenbruk den så mange ganger du vil. Følgende eksempel trekker ut for- og etternavnet fra inngangsstrukturen, antyder dem og lagrer dem i variablene $ F og $ L. Så gjør den dem i normal og omvendt rekkefølge. 

Et annet pent trick her er at jeg sender en anonym struktur til malen for å gjøre koden mer konsistent, og unngå å forklare det med typer som bare brukes på ett sted.

funk main () t: = template.New ("") t.Parse ('$ F: = .FirstName | printf "% q" $ L: = .LastName | printf "% q"  Normal: $ F $ L Omvendt: $ L $ F ') t.Execute (os.Stdout, struct FirstName string LastName string "Gigi "," Sayfan ",) Utgang: Normal:" Gigi "" Sayfan "Omvendt:" Sayfan "" Gigi "

conditionals

Men la oss ikke stoppe her. Du kan til og med ha forhold i malene dine. Det er en hvis-end handling og if-else-end handling. If-klausulen vises hvis utgangen av den betingede rørledningen ikke er tom:

func main () t: = template.New ("") t.Parse ('if. . else Ingen data er tilgjengelig end') t. Utfør (OS.Stdout, "42") t.Kjøre (os.Stdout, "") Utgang: 42 Ingen data tilgjengelig 

Merk at ellers klausulen fører til en ny linje, og teksten "Ingen data tilgjengelig" er betydelig innrykket.

Loops

Gå maler har også sløyfer. Dette er veldig nyttig når dataene inneholder skiver, kart eller andre iterables. Dataobjektet for en sløyfe kan være noen iterable Go-objekt som array, skive, kart eller kanal. Områdefunksjonen lar deg iterere over dataobjektet og opprette en utgang for hvert element. La oss se hvordan å iterere over et kart:

func main () t: = template.New ("") e: = 'Navn, Scores område $ k, $ v: =. $ k range $ s: = $ v , $ s end end 't.Parse (e) t.Kjøre (os.Stdout, map [string] [] int "Mike": 88, 77 , 99, "Betty": 54, 96, 78, "Jake": 89, 67, 93,) Utgang: Navn, Scores Betty, 54,96,78 Jake, 89,67,93 Mike, 88,77,99 

Som du ser, er det ledende hvite rommet fortsatt et problem. Jeg var ikke i stand til å finne en anstendig måte å adressere den i malens syntaks. Det vil kreve etterbehandling. I teorien kan du plassere et dash for å trimme whitespace foregående eller følgende handlinger, men det virker ikke i nærvær av område.

Tekstmaler

Tekstmaler er implementert i tekst- / malpakken. I tillegg til alt vi har sett så langt, kan denne pakken også laste inn maler fra filer og komponere flere maler ved hjelp av mal-handlingen. Sjablongobjektet har mange metoder for å støtte slike avanserte brukstilfeller:

  • ParseFiles ()
  • ParseGlob ()
  • AddParseTree ()
  • Klon ()
  • DefinedTemplates ()
  • Delims ()
  • ExecuteTemplate ()
  • Funcs ()
  • Se opp()
  • Alternativ()
  • Maler ()

På grunn av plassbegrensninger vil jeg ikke gå nærmere (kanskje i en annen opplæring).

HTML-maler 

HTML-maler er definert i html / malpakken. Den har nøyaktig det samme grensesnittet som tekstmalpakken, men det er laget for å generere HTML som er trygt fra kodeinjeksjon. Dette gjøres ved omhyggeliggjøring av dataene før det legges inn i malen. Arbeidsforutsetningen er at malforfattere er klarert, men dataene som leveres til malen, kan ikke stole på. 

Dette er viktig. Hvis du automatisk bruker maler du mottar fra usikre kilder, vil html / malpakken ikke beskytte deg. Det er ditt ansvar å sjekke malene.

La oss se forskjellen mellom utgangen av tekst / mal og html / mal. Når du bruker teksten / mal, er det enkelt å injisere JavaScript-kode i den genererte produksjonen.

pakke viktigste import ("tekst / mal" "os") func main () t, _: = template.New (""). Parse ("Hei, .!") d: = ""t.Execute (os.Stdout, d) Utgang: Hei, ! 

Men import av html / mal i stedet for tekst / mal hindrer dette angrepet, ved å rømme skriptet og parentesene:

Hallo, !

Å håndtere feil

Det er to typer feil: parsing feil og utførelsesfeil. De Parse () funksjonen analyserer malteksten og returnerer en feil, som jeg ignorerte i kodeprøvene, men i produksjonskode vil du fange disse feilene tidlig og adressere dem. 

Hvis du vil ha en rask og skitten utgang da Må() Metoden tar produksjonen av en metode som returnerer (* Mal, feil)-som Klon (), Parse (), eller ParseFiles ()-og panikk hvis feilen ikke er null. Slik ser du etter en eksplisitt parsingsfeil:

func main () e: = "Jeg er en dårlig mal, " _, err: = template.New (""). Parse (e) hvis err! = null msg: = "Mislyktes parsing: '% s'. \ nFeil:% v \ n "fmt.Printf (msg, e, err) Output: Feil å analysere mal:" Jeg er en dårlig mal, '. Feil: mal:: 1: Uventet unclosed action i kommando 

Ved hjelp av Må() bare panikk hvis noe er galt med malen:

func main () e: = "Jeg er en dårlig mal, " template.Must (template.New (""). Parse (e)) Utgang: panikk: mal:: 1: uventet lukket handling i kommando 

Den andre typen feil er en utførelsesfeil hvis de oppgitte dataene ikke samsvarer med malingen. Igjen kan du sjekke eksplisitt eller bruke Må() å få panikk. Jeg anbefaler i dette tilfellet at du sjekker og har en gjenopprettingsmekanisme på plass. 

Vanligvis er det ikke nødvendig å bringe hele systemet ned bare fordi en inngang ikke oppfyller kravene. I det følgende eksemplet forventer malen et felt som kalles Navn på datastrukturen, men jeg gir en struktur med et felt som kalles Fullt navn.

func main () e: = "Det må være et navn: .Name" t, _: = template.New (""). Parse (e) err: = t.Execute (os.Stdout, struct FullName string "Gigi Sayfan",) hvis err! = null fmt.Println ("Feil å utføre.", feil) Utdata: Det må være et navn: Feil å utføre. mal:: 1: 24: kjører "" på <.Name>: kan ikke evaluere felt Navn i type struct FullName string 

Konklusjon

Go har et kraftig og sofistikert templeringssystem. Det brukes til stor effekt i mange store prosjekter som Kubernetes og Hugo. Html / malpakken gir en sikker, industriell styrkefasilitet for å sanitere produksjonen av web-baserte systemer. I denne opplæringen dekket vi alle grunnleggende og noen mellomliggende tilfeller. 

Det er fortsatt mer avanserte funksjoner i malpakkerne som venter på å være låst opp. Spill med maler og innlemme dem i programmene dine. Du vil bli hyggelig overrasket over hvor konsistent og lesbar din tekstgenereringskode ser ut.