The original idea was to spend the bulk of class time discussing core concepts and production systems, with some discussion of implementation in Go. The assignments would cover implementation.
Rebooting the course: focus on implementation of the scale model. We'll still discuss general principles and production systems, but it'll be deemphasized.
Simple build shell script:
.sh
extension.
#! /bin/bash
at beginning of the file to tell the loader that it's a shell
script.
chmod
command
Make: original build tool from Bell Labs. Long in the tooth, but still useful
VAR = value
target : dependencies...
tabshell commands using $(VAR) is needed
make install
make test
make clean
go
command does this for go programs, but a system may be more than
just the Go source
go install
,
not
go build
or
go run
go fmt
(no points off)
Because the golang web server handles concurrent requests (even if you're not making concurrent requests in your testing), you need to protect shared memory access.
Everything you need to know about concurrency for the purposes of this course
sync
package provides a
Mutex
object ("mutual exclusion")
Lock
the mutex at the beginning of the
critical section
and
Unlock
when the section completes
sync.RWMutex
allows multiple concurrent readers, but stops everything for a
single write
I was assuming everone knows what a cookie is. Using a cookie in Go is just a struct and couple of library calls:
Artists' studies are used by artists to figure out smaller pieces of larger scenes. By analogy, when there is a library or feature you don't quite understand, write a small, simple program to test it out. Once you undestand it, you can incorporate it into your program.
You don't get credit on the assignment for doing a cookie study, but a study may help you figure out what you're trying to do
package main
import (
"fmt"
"net/http"
"time"
)
func handler(resp http.ResponseWriter, req *http.Request) {
oldCookie, err := req.Cookie("name")
if err != nil {
fmt.Printf("error getting old cookie: %v\n", err)
} else {
fmt.Printf("old cookie: %v\n", oldCookie)
}
newCookie := &http.Cookie{
Name: "name", // how original
Value: "Aries_was_here",
Expires: time.Now().Add(3 * time.Minute),
}
http.SetCookie(resp, newCookie)
fmt.Fprintf(resp, "cookie: %v", newCookie)
}
func main() {
http.HandleFunc("/", handler)
err := http.ListenAndServe(":9191", nil)
fmt.Printf("ListenAndServe: %v\n", err)
}
The
curl
command is a handy tool for testing and debugging web sites. It
fetches the data without doing any rendering, so you can figure out
what's going on.
curl -I
just fetches the headers.
The format argument in the formatted print commands in the
fmt
package (which are direct descendants of C)
is a kind of template.
The Go language provides two templating packages:
text/template
and
html/template
.
The latter builds on the former, to provide automagic escaping of
template parameters. You need to read both pages.
Templates are fairly powerful, including conditionals and loops, but we only need to use fairly limited functionality.
Let's do some artists' studies. Start with turning the first
example in the
html/template
into a working program:
package main
import (
"fmt"
"html/template"
"os"
)
const (
rawText = `{{define "theTemplate"}}Hello, {{.}}!{{end}}`
)
func main() {
tmpl := template.New("foo")
tmpl, err := tmpl.Parse(rawText)
if err != nil {
fmt.Printf("parsing template: %s\n", err)
return
}
err = tmpl.ExecuteTemplate(os.Stdout, "theTemplate", "Zeus")
if err != nil {
fmt.Printf("executing template: %s\n", err)
return
}
fmt.Println()
}
Hello, Zeus!
Figuring out the context argument:
package main
import (
"fmt"
"html/template"
"os"
)
const (
rawText = `{{define "theTemplate"}}Hello, {{.Nationality}} {{.Kind}}!{{end}}`
)
type Context struct {
Nationality string
Kind string
}
func main() {
tmpl := template.New("foo")
tmpl, err := tmpl.Parse(rawText)
if err != nil {
fmt.Printf("parsing template: %s\n", err)
return
}
context1 := Context{
Nationality: "Greek",
Kind: "Titans",
}
err = tmpl.ExecuteTemplate(os.Stdout, "theTemplate", context1)
if err != nil {
fmt.Printf("executing template: %s\n", err)
return
}
fmt.Println()
context2 := Context{
Nationality: "Norse",
Kind: "Giants",
}
err = tmpl.ExecuteTemplate(os.Stdout, "theTemplate", context2)
if err != nil {
fmt.Printf("executing template: %s\n", err)
return
}
fmt.Println()
}
Hello, Greek Titans!
Hello, Norse Giants!
Read the template from a file using the
ParseFiles
method:
package main
import (
"fmt"
"html/template"
"os"
)
const (
TemplateFileName = "templates/example03.tmpl"
)
type Context struct {
Nationality string
Kind string
}
func main() {
tmpl := template.New("foo")
tmpl, err := tmpl.ParseFiles(TemplateFileName)
if err != nil {
fmt.Printf("parsing template: %s\n", err)
return
}
context1 := Context{
Nationality: "Roman",
Kind: "Gods",
}
err = tmpl.ExecuteTemplate(os.Stdout, "theTemplate", context1)
if err != nil {
fmt.Printf("executing template: %s\n", err)
return
}
fmt.Println()
}
{{define "theTemplate"}}Hello, {{.Nationality}} {{.Kind}}!{{end}}
Hello, Roman Gods!
Read from multiple template files. Note
...
turns a slice into a sequence of arguments:
package main
import (
"fmt"
"html/template"
"os"
)
var (
TemplateFiles = []string{
"templates/example04.tmpl",
"templates/example04-body.tmpl",
}
)
type Context struct {
Name string
}
func main() {
tmpl := template.New("foo")
tmpl, err := tmpl.ParseFiles(TemplateFiles...)
if err != nil {
fmt.Printf("parsing template: %s\n", err)
return
}
context1 := Context{
Name: "Hephaestus",
}
err = tmpl.ExecuteTemplate(os.Stdout, "page", context1)
if err != nil {
fmt.Printf("executing template: %s\n", err)
return
}
fmt.Println()
}
templates/example04.tmpl
:
{{define "page"}}
<html>
<body>
<p>
{{template "body" .}}
</p>
{{end}}
templates/example04-body.tmpl
:
{{define "body"}}Hello, {{.Name}}!{{end}}`
Output:
<html>
<body>
<p>
Hello, Hephaestus!
</p>
This is overkill for the assignment, but to get carried away:
package main
import (
"fmt"
"html/template"
"os"
)
var (
TemplateFiles = []string{
"templates/example05.tmpl",
}
)
type Context struct {
Nationality string
Kind string
Names []string
}
func main() {
tmpl := template.New("foo")
tmpl, err := tmpl.ParseFiles(TemplateFiles...)
if err != nil {
fmt.Printf("parsing template: %s\n", err)
return
}
context1 := Context{
Nationality: "Greek",
Kind: "Gods",
Names: []string{
"Zeus",
"Ares",
"Apollo",
"Hera",
"Aphordite",
"Athena",
},
}
err = tmpl.ExecuteTemplate(os.Stdout, "page", context1)
if err != nil {
fmt.Printf("executing template: %s\n", err)
return
}
fmt.Println()
err = tmpl.ExecuteTemplate(os.Stdout, "name", "Loki")
if err != nil {
fmt.Printf("executing template: %s\n", err)
return
}
fmt.Println()
}
templates/example05.tmpl
:
{{define "page"}}
<html>
<body>
{{template "body" .}}
</body>
{{end}}
{{define "body"}}
<h1>{{.Nationality}} {{.Kind}}</h1>
<ul>
{{range .Names}}{{template "name" .}}{{end}}
</ul>
{{end}}
{{define "name"}}
<li>{{.}}</li>
{{end}}
Output:
./bin/template05
<html>
<body>
<h1>Greek Gods</h1>
<ul>
<li>Zeus</li>
<li>Ares</li>
<li>Apollo</li>
<li>Hera</li>
<li>Aphordite</li>
<li>Athena</li>
</ul>
</body>
<li>Loki</li>