Hugo, a static site generator.
Hugo, in turn, is written in Go and uses the Go text/template
package. So let’s root around in there a bit and see what’s going on.
This will serve as sort of an introduction to Hugo, Go, and the text/template
package.
*While writing this, I learned that you should search for golang
and not go
when browsing the internet.*
The text/template
package uses double curly brackets to replace text in a string. All other text is literally copied over. This is pretty similar to Angular templates or Mustache.js?
Here’s a programmatic example, shamelessly copied from the Go documentation:
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) }
I see that we have a type
keyword, we define an Inventory
and I think it’s a struct
(whatever that means).
Looks awfully similar to a template in Typescript. My C/C++ is pretty weak, but I’d imagine that a Go struct
is similar. Something like data without behavior. If it had behavior it would be a so-called “class”.
Go
verviewWelcome to the Thundergo
me?
I’m going to dig in to all of the words I don’t know about. Lucky you.
type
struct
:=
temp, err
nil
valuespanic
functionos
and Stdout
We used the type
keyword to create a type declaration for the Inventory
type, which is a struct
.
Type declarations can be either an alias declaration or a type definition. Let’s look at the type definition
We’ll concern ourselves just with the type declarations.
A type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it.
In our example from the go documentation, we created a new type with the same underlying type as a struct
and with the identifier Inventory
.
Go structs are collections of fields – data without behavior. Some initial reading shows that Go does not have classes – I wonder if structs support inheritance, or attaching methods to the struct?
The :=
operator looks like crazy assignment. We don’t need a declaration keyword or a declared type, which is nice. I hear a lot of people are using Go at the enterprise so I bet it’s not very weakly typed – maybe uses something like duck-typing in Python.
Wikipedia says that Go uses structural typing so I’m not sure what that means yet. It looks like duck typing is a looser form of typing, where only the parts of the type that are accessed are actually checked. In structural typing the entire structure needs to match.
I wonder if Python is actually duck typed?
Turns out, :=
is crazy assignment, it’s a short variable declaration.
These are all equivalent:
var a int = 10
var a = 10
a := 10
The assignment of temp, err
is a tuple assignment and follows some rules.
Which means that template.new("test")
returns multiple values, the resulting string and a possible error.
nil
valueThe built-in function panic
is how Go does error handling. I’m so into that.
os
packageGo provides a wrapper around operating system functionality in the os
package.
It’s supposed to be platform indepenedent so it should work on Windows, Mac, and GNU+Linux.
Stdout
means the standard out file descriptor.
I’ve never taken an Operating Systems course, so I don’t actually know what a “file descriptor” is.
text/template
Now that we know everything about Go, we can dig into the text/template
package some more.
We can trim whitespace around operators using the -
sign:
"{{34 -}} > {{- 26}}" // "34>26"
"{{34}} > {{26}}" // "34 > 26"
"{{34-}} > {{-26}}" // "34- > -26"
Go templates are data-driven templates producing textual output. We define the data in, the template data, and get string data out. Instead of procedurally defining how things should be layed out. So, we declare a data structure, define properties on it, pass that to a template definition, then receive a string output.
This is only sort of true, since we do have some imperative/procedural statements.
Keywords:
{{
and }}
by default. Can contain arguments and pipelines. There are options for if/elsing ranges, defining and using templates, block shorthand for templates, and using with
which is like if
but not since it grabs the cursor, called dot
.|
) to compose!$
and shorthand assignment: $variable := pipeline
Here are some built in functions to use as pipelines:
Boolean functions:
You can nest templates, so a template definition can contain other template invocations(?) Again, copied from the documentation:
`{{define "T1"}}ONE{{end}}
{{define "T2"}}TWO{{end}}
{{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}}
{{template "T3"}}`