diff options
Diffstat (limited to 'doc/codelab/wiki')
-rw-r--r-- | doc/codelab/wiki/Makefile | 9 | ||||
-rw-r--r-- | doc/codelab/wiki/edit.html | 6 | ||||
-rw-r--r-- | doc/codelab/wiki/final-noclosure.go | 22 | ||||
-rw-r--r-- | doc/codelab/wiki/final-noerror.go | 18 | ||||
-rw-r--r-- | doc/codelab/wiki/final-parsetemplate.go | 22 | ||||
-rw-r--r-- | doc/codelab/wiki/final-template.go | 22 | ||||
-rw-r--r-- | doc/codelab/wiki/final.go | 22 | ||||
-rw-r--r-- | doc/codelab/wiki/get.go | 50 | ||||
-rw-r--r-- | doc/codelab/wiki/htmlify.go | 2 | ||||
-rw-r--r-- | doc/codelab/wiki/index.html | 117 | ||||
-rw-r--r-- | doc/codelab/wiki/notemplate.go | 22 | ||||
-rw-r--r-- | doc/codelab/wiki/part1-noerror.go | 20 | ||||
-rw-r--r-- | doc/codelab/wiki/part1.go | 20 | ||||
-rw-r--r-- | doc/codelab/wiki/part2.go | 18 | ||||
-rw-r--r-- | doc/codelab/wiki/srcextract.go | 8 | ||||
-rwxr-xr-x | doc/codelab/wiki/test.sh | 35 | ||||
-rw-r--r-- | doc/codelab/wiki/view.html | 6 | ||||
-rw-r--r-- | doc/codelab/wiki/wiki.html | 56 |
18 files changed, 265 insertions, 210 deletions
diff --git a/doc/codelab/wiki/Makefile b/doc/codelab/wiki/Makefile index e0549fc8e..0d948ed4b 100644 --- a/doc/codelab/wiki/Makefile +++ b/doc/codelab/wiki/Makefile @@ -13,12 +13,13 @@ CLEANFILES+=index.html srcextract.bin htmlify.bin index.html: srcextract.bin htmlify.bin awk '/^!/{system(substr($$0,2)); next} {print}' "$$@" < wiki.html > index.html -test: final.bin - ./test.sh - rm -f final.6 final.bin +test: get.bin + bash ./test.sh + rm -f get.6 get.bin %.bin: %.$O $(LD) -o $@ $< -%.$O: + +%.$O: %.go $(GC) $*.go diff --git a/doc/codelab/wiki/edit.html b/doc/codelab/wiki/edit.html index 71a919496..7a5768ce9 100644 --- a/doc/codelab/wiki/edit.html +++ b/doc/codelab/wiki/edit.html @@ -1,6 +1,6 @@ -<h1>Editing {title}</h1> +<h1>Editing {Title}</h1> -<form action="/save/{title}" method="POST"> -<div><textarea name="body" rows="20" cols="80">{body|html}</textarea></div> +<form action="/save/{Title}" method="POST"> +<div><textarea name="body" rows="20" cols="80">{Body|html}</textarea></div> <div><input type="submit" value="Save"></div> </form> diff --git a/doc/codelab/wiki/final-noclosure.go b/doc/codelab/wiki/final-noclosure.go index 2f48565ca..99121f298 100644 --- a/doc/codelab/wiki/final-noclosure.go +++ b/doc/codelab/wiki/final-noclosure.go @@ -8,23 +8,23 @@ import ( "template" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } func viewHandler(w http.ResponseWriter, r *http.Request) { @@ -47,7 +47,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) { } p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } @@ -58,7 +58,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { return } body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} err = p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -67,7 +67,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/view/"+title, http.StatusFound) } -func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, err := template.ParseFile(tmpl+".html", nil) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) diff --git a/doc/codelab/wiki/final-noerror.go b/doc/codelab/wiki/final-noerror.go index cf4852265..0f18912d2 100644 --- a/doc/codelab/wiki/final-noerror.go +++ b/doc/codelab/wiki/final-noerror.go @@ -7,23 +7,23 @@ import ( "template" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } const lenPath = len("/view/") @@ -32,7 +32,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } t, _ := template.ParseFile("edit.html", nil) t.Execute(p, w) diff --git a/doc/codelab/wiki/final-parsetemplate.go b/doc/codelab/wiki/final-parsetemplate.go index f02d116b2..ea8977601 100644 --- a/doc/codelab/wiki/final-parsetemplate.go +++ b/doc/codelab/wiki/final-parsetemplate.go @@ -8,23 +8,23 @@ import ( "template" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } func viewHandler(w http.ResponseWriter, r *http.Request, title string) { @@ -39,14 +39,14 @@ func viewHandler(w http.ResponseWriter, r *http.Request, title string) { func editHandler(w http.ResponseWriter, r *http.Request, title string) { p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } func saveHandler(w http.ResponseWriter, r *http.Request, title string) { body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} err := p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -55,7 +55,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request, title string) { http.Redirect(w, r, "/view/"+title, http.StatusFound) } -func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, err := template.ParseFile(tmpl+".html", nil) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) diff --git a/doc/codelab/wiki/final-template.go b/doc/codelab/wiki/final-template.go index 0bb133d3a..4d6a2cfab 100644 --- a/doc/codelab/wiki/final-template.go +++ b/doc/codelab/wiki/final-template.go @@ -7,23 +7,23 @@ import ( "template" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } const lenPath = len("/view/") @@ -32,7 +32,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } @@ -46,12 +46,12 @@ func viewHandler(w http.ResponseWriter, r *http.Request) { func saveHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} p.save() http.Redirect(w, r, "/view/"+title, http.StatusFound) } -func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, _ := template.ParseFile(tmpl+".html", nil) t.Execute(p, w) } diff --git a/doc/codelab/wiki/final.go b/doc/codelab/wiki/final.go index 0c0206bc0..8ecd97d74 100644 --- a/doc/codelab/wiki/final.go +++ b/doc/codelab/wiki/final.go @@ -8,23 +8,23 @@ import ( "template" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } func viewHandler(w http.ResponseWriter, r *http.Request, title string) { @@ -39,14 +39,14 @@ func viewHandler(w http.ResponseWriter, r *http.Request, title string) { func editHandler(w http.ResponseWriter, r *http.Request, title string) { p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } func saveHandler(w http.ResponseWriter, r *http.Request, title string) { body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} err := p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -63,7 +63,7 @@ func init() { } } -func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { err := templates[tmpl].Execute(p, w) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) diff --git a/doc/codelab/wiki/get.go b/doc/codelab/wiki/get.go new file mode 100644 index 000000000..342831416 --- /dev/null +++ b/doc/codelab/wiki/get.go @@ -0,0 +1,50 @@ +package main + +import ( + "http" + "flag" + "fmt" + "io" + "log" + "net" + "os" + "strings" +) + +var ( + post = flag.String("post", "", "urlencoded form data to POST") + addr = flag.Bool("addr", false, "find open address and print to stdout") +) + +func main() { + flag.Parse() + if *addr { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + log.Fatal(err) + } + defer l.Close() + fmt.Print(l.Addr()) + return + } + url := flag.Arg(0) + if url == "" { + log.Fatal("no url supplied") + } + var r *http.Response + var err os.Error + if *post != "" { + b := strings.NewReader(*post) + r, err = http.Post(url, "application/x-www-form-urlencoded", b) + } else { + r, _, err = http.Get(url) + } + if err != nil { + log.Fatal(err) + } + defer r.Body.Close() + _, err = io.Copy(os.Stdout, r.Body) + if err != nil { + log.Fatal(err) + } +} diff --git a/doc/codelab/wiki/htmlify.go b/doc/codelab/wiki/htmlify.go index 4a52e077f..456d06fd5 100644 --- a/doc/codelab/wiki/htmlify.go +++ b/doc/codelab/wiki/htmlify.go @@ -8,5 +8,5 @@ import ( func main() { b, _ := ioutil.ReadAll(os.Stdin) - template.HTMLFormatter(os.Stdout, b, "") + template.HTMLFormatter(os.Stdout, "", b) } diff --git a/doc/codelab/wiki/index.html b/doc/codelab/wiki/index.html index c494a3ced..fe99c32d1 100644 --- a/doc/codelab/wiki/index.html +++ b/doc/codelab/wiki/index.html @@ -71,14 +71,14 @@ declaration. <p> Let's start by defining the data structures. A wiki consists of a series of interconnected pages, each of which has a title and a body (the page content). -Here, we define <code>page</code> as a struct with two fields representing +Here, we define <code>Page</code> as a struct with two fields representing the title and body. </p> <pre> -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } </pre> @@ -86,33 +86,33 @@ type page struct { The type <code>[]byte</code> means "a <code>byte</code> slice". (See <a href="http://golang.org/doc/effective_go.html#slices">Effective Go</a> for more on slices.) -The <code>body</code> element is a <code>[]byte</code> rather than +The <code>Body</code> element is a <code>[]byte</code> rather than <code>string</code> because that is the type expected by the <code>io</code> libraries we will use, as you'll see below. </p> <p> -The <code>page</code> struct describes how page data will be stored in memory. +The <code>Page</code> struct describes how page data will be stored in memory. But what about persistent storage? We can address that by creating a -<code>save</code> method on <code>page</code>: +<code>save</code> method on <code>Page</code>: </p> <pre> -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } </pre> <p> This method's signature reads: "This is a method named <code>save</code> that -takes as its receiver <code>p</code>, a pointer to <code>page</code> . It takes +takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes no parameters, and returns a value of type <code>os.Error</code>." </p> <p> -This method will save the <code>page</code>'s <code>body</code> to a text -file. For simplicity, we will use the <code>title</code> as the file name. +This method will save the <code>Page</code>'s <code>Body</code> to a text +file. For simplicity, we will use the <code>Title</code> as the file name. </p> <p> @@ -120,7 +120,7 @@ The <code>save</code> method returns an <code>os.Error</code> value because that is the return type of <code>WriteFile</code> (a standard library function that writes a byte slice to a file). The <code>save</code> method returns the error value, to let the application handle it should anything go wrong while -writing the file. If all goes well, <code>page.save()</code> will return +writing the file. If all goes well, <code>Page.save()</code> will return <code>nil</code> (the zero-value for pointers, interfaces, and some other types). </p> @@ -137,17 +137,17 @@ We will want to load pages, too: </p> <pre> -func loadPage(title string) *page { +func loadPage(title string) *Page { filename := title + ".txt" body, _ := ioutil.ReadFile(filename) - return &page{title: title, body: body} + return &Page{Title: title, Body: body} } </pre> <p> The function <code>loadPage</code> constructs the file name from -<code>title</code>, reads the file's contents into a new -<code>page</code>, and returns a pointer to that new <code>page</code>. +<code>Title</code>, reads the file's contents into a new +<code>Page</code>, and returns a pointer to that new <code>page</code>. </p> <p> @@ -161,23 +161,23 @@ error return value (in essence, assigning the value to nothing). <p> But what happens if <code>ReadFile</code> encounters an error? For example, the file might not exist. We should not ignore such errors. Let's modify the -function to return <code>*page</code> and <code>os.Error</code>. +function to return <code>*Page</code> and <code>os.Error</code>. </p> <pre> -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } </pre> <p> Callers of this function can now check the second parameter; if it is -<code>nil</code> then it has successfully loaded a page. If not, it will be an +<code>nil</code> then it has successfully loaded a Page. If not, it will be an <code>os.Error</code> that can be handled by the caller (see the <a href="http://golang.org/pkg/os/#Error">os package documentation</a> for details). @@ -191,17 +191,17 @@ written: <pre> func main() { - p1 := &page{title: "TestPage", body: []byte("This is a sample page.")} + p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")} p1.save() p2, _ := loadPage("TestPage") - fmt.Println(string(p2.body)) + fmt.Println(string(p2.Body)) } </pre> <p> After compiling and executing this code, a file named <code>TestPage.txt</code> would be created, containing the contents of <code>p1</code>. The file would -then be read into the struct <code>p2</code>, and its <code>body</code> element +then be read into the struct <code>p2</code>, and its <code>Body</code> element printed to the screen. </p> @@ -317,7 +317,7 @@ const lenPath = len("/view/") func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) - fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.title, p.body) + fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body) } </pre> @@ -377,7 +377,7 @@ href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code should show a page titled "test" containing the words "Hello world". </p> -<h2>Editing pages</h2> +<h2>Editing Pages</h2> <p> A wiki is not a wiki without the ability to edit pages. Let's create two new @@ -401,7 +401,7 @@ func main() { <p> The function <code>editHandler</code> loads the page -(or, if it doesn't exist, create an empty <code>page</code> struct), +(or, if it doesn't exist, create an empty <code>Page</code> struct), and displays an HTML form. </p> @@ -410,14 +410,14 @@ func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } fmt.Fprintf(w, "<h1>Editing %s</h1>"+ "<form action=\"/save/%s\" method=\"POST\">"+ "<textarea name=\"body\">%s</textarea><br>"+ "<input type=\"submit\" value=\"Save\">"+ "</form>", - p.title, p.title, p.body) + p.Title, p.Title, p.Body) } </pre> @@ -454,10 +454,10 @@ Open a new file named <code>edit.html</code>, and add the following lines: </p> <pre> -<h1>Editing {title}</h1> +<h1>Editing {Title}</h1> -<form action="/save/{title}" method="POST"> -<div><textarea name="body" rows="20" cols="80">{body|html}</textarea></div> +<form action="/save/{Title}" method="POST"> +<div><textarea name="body" rows="20" cols="80">{Body|html}</textarea></div> <div><input type="submit" value="Save"></div> </form> </pre> @@ -472,7 +472,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } t, _ := template.ParseFile("edit.html", nil) t.Execute(p, w) @@ -486,15 +486,15 @@ The function <code>template.ParseFile</code> will read the contents of <p> The method <code>t.Execute</code> replaces all occurrences of -<code>{title}</code> and <code>{body}</code> with the values of -<code>p.title</code> and <code>p.body</code>, and writes the resultant +<code>{Title}</code> and <code>{Body}</code> with the values of +<code>p.Title</code> and <code>p.Body</code>, and writes the resultant HTML to the <code>http.ResponseWriter</code>. </p> <p> -Note that we've used <code>{body|html}</code> in the above template. +Note that we've used <code>{Body|html}</code> in the above template. The <code>|html</code> part asks the template engine to pass the value -<code>body</code> through the <code>html</code> formatter before outputting it, +<code>Body</code> through the <code>html</code> formatter before outputting it, which escapes HTML characters (such as replacing <code>></code> with <code>&gt;</code>). This will prevent user data from corrupting the form HTML. @@ -511,11 +511,11 @@ While we're working with templates, let's create a template for our </p> <pre> -<h1>{title}</h1> +<h1>{Title}</h1> -<p>[<a href="/edit/{title}">edit</a>]</p> +<p>[<a href="/edit/{Title}">edit</a>]</p> -<div>{body}</div> +<div>{Body}</div> </pre> <p> @@ -548,12 +548,12 @@ func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } -func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, _ := template.ParseFile(tmpl+".html", nil) t.Execute(p, w) } @@ -568,8 +568,8 @@ The handlers are now shorter and simpler. <p> What if you visit <code>/view/APageThatDoesntExist</code>? The program will crash. This is because it ignores the error return value from -<code>loadPage</code>. Instead, if the requested page doesn't exist, it should -redirect the client to the edit page so the content may be created: +<code>loadPage</code>. Instead, if the requested Page doesn't exist, it should +redirect the client to the edit Page so the content may be created: </p> <pre> @@ -589,7 +589,7 @@ The <code>http.Redirect</code> function adds an HTTP status code of header to the HTTP response. </p> -<h2>Saving pages</h2> +<h2>Saving Pages</h2> <p> The function <code>saveHandler</code> will handle the form submission. @@ -599,7 +599,7 @@ The function <code>saveHandler</code> will handle the form submission. func saveHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} p.save() http.Redirect(w, r, "/view/"+title, http.StatusFound) } @@ -607,7 +607,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { <p> The page title (provided in the URL) and the form's only field, -<code>body</code>, are stored in a new <code>page</code>. +<code>Body</code>, are stored in a new <code>Page</code>. The <code>save()</code> method is then called to write the data to a file, and the client is redirected to the <code>/view/</code> page. </p> @@ -615,7 +615,7 @@ and the client is redirected to the <code>/view/</code> page. <p> The value returned by <code>FormValue</code> is of type <code>string</code>. We must convert that value to <code>[]byte</code> before it will fit into -the <code>page</code> struct. We use <code>[]byte(body)</code> to perform +the <code>Page</code> struct. We use <code>[]byte(body)</code> to perform the conversion. </p> @@ -634,7 +634,7 @@ First, let's handle the errors in <code>renderTemplate</code>: </p> <pre> -func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { t, err := template.ParseFile(tmpl+".html", nil) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -660,7 +660,7 @@ Now let's fix up <code>saveHandler</code>: <pre> func saveHandler(w http.ResponseWriter, r *http.Request, title string) { body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} err := p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -725,7 +725,7 @@ the <code>Execute</code> method on the appropriate <code>Template</code> from <code>templates</code>: <pre> -func renderTemplate(w http.ResponseWriter, tmpl string, p *page) { +func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { err := templates[tmpl].Execute(p, w) if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -747,7 +747,6 @@ Then we can create a global variable to store our validation regexp: </p> <pre> -var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$") </pre> <p> @@ -761,7 +760,7 @@ the expression compilation fails, while <code>Compile</code> returns an <p> Now, let's write a function that extracts the title string from the request -URL, and tests it against our <code>titleValidator</code> expression: +URL, and tests it against our <code>TitleValidator</code> expression: </p> <pre> @@ -807,7 +806,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) { } p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } @@ -818,7 +817,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { return } body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} err = p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) @@ -895,7 +894,7 @@ The closure returned by <code>makeHandler</code> is a function that takes an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other words, an <code>http.HandlerFunc</code>). The closure extracts the <code>title</code> from the request path, and -validates it with the <code>titleValidator</code> regexp. If the +validates it with the <code>TitleValidator</code> regexp. If the <code>title</code> is invalid, an error will be written to the <code>ResponseWriter</code> using the <code>http.NotFound</code> function. If the <code>title</code> is valid, the enclosed handler function @@ -936,14 +935,14 @@ func viewHandler(w http.ResponseWriter, r *http.Request, title string) { func editHandler(w http.ResponseWriter, r *http.Request, title string) { p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } renderTemplate(w, "edit", p) } func saveHandler(w http.ResponseWriter, r *http.Request, title string) { body := r.FormValue("body") - p := &page{title: title, body: []byte(body)} + p := &Page{Title: title, Body: []byte(body)} err := p.save() if err != nil { http.Error(w, err.String(), http.StatusInternalServerError) diff --git a/doc/codelab/wiki/notemplate.go b/doc/codelab/wiki/notemplate.go index c1f952c83..9cbe9ad76 100644 --- a/doc/codelab/wiki/notemplate.go +++ b/doc/codelab/wiki/notemplate.go @@ -7,23 +7,23 @@ import ( "os" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } const lenPath = len("/view/") @@ -31,21 +31,21 @@ const lenPath = len("/view/") func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) - fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.title, p.body) + fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body) } func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, err := loadPage(title) if err != nil { - p = &page{title: title} + p = &Page{Title: title} } fmt.Fprintf(w, "<h1>Editing %s</h1>"+ "<form action=\"/save/%s\" method=\"POST\">"+ "<textarea name=\"body\">%s</textarea><br>"+ "<input type=\"submit\" value=\"Save\">"+ "</form>", - p.title, p.title, p.body) + p.Title, p.Title, p.Body) } func main() { diff --git a/doc/codelab/wiki/part1-noerror.go b/doc/codelab/wiki/part1-noerror.go index 39e8331e3..14cfc321a 100644 --- a/doc/codelab/wiki/part1-noerror.go +++ b/doc/codelab/wiki/part1-noerror.go @@ -6,25 +6,25 @@ import ( "os" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) *page { +func loadPage(title string) *Page { filename := title + ".txt" body, _ := ioutil.ReadFile(filename) - return &page{title: title, body: body} + return &Page{Title: title, Body: body} } func main() { - p1 := &page{title: "TestPage", body: []byte("This is a sample page.")} + p1 := &Page{Title: "TestPage", Body: []byte("This is a sample page.")} p1.save() p2 := loadPage("TestPage") - fmt.Println(string(p2.body)) + fmt.Println(string(p2.Body)) } diff --git a/doc/codelab/wiki/part1.go b/doc/codelab/wiki/part1.go index f3678baa5..4b0654f8b 100644 --- a/doc/codelab/wiki/part1.go +++ b/doc/codelab/wiki/part1.go @@ -6,28 +6,28 @@ import ( "os" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } func main() { - p1 := &page{title: "TestPage", body: []byte("This is a sample page.")} + p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")} p1.save() p2, _ := loadPage("TestPage") - fmt.Println(string(p2.body)) + fmt.Println(string(p2.Body)) } diff --git a/doc/codelab/wiki/part2.go b/doc/codelab/wiki/part2.go index 8d4454a74..d57c3a01f 100644 --- a/doc/codelab/wiki/part2.go +++ b/doc/codelab/wiki/part2.go @@ -7,23 +7,23 @@ import ( "os" ) -type page struct { - title string - body []byte +type Page struct { + Title string + Body []byte } -func (p *page) save() os.Error { - filename := p.title + ".txt" - return ioutil.WriteFile(filename, p.body, 0600) +func (p *Page) save() os.Error { + filename := p.Title + ".txt" + return ioutil.WriteFile(filename, p.Body, 0600) } -func loadPage(title string) (*page, os.Error) { +func loadPage(title string) (*Page, os.Error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - return &page{title: title, body: body}, nil + return &Page{Title: title, Body: body}, nil } const lenPath = len("/view/") @@ -31,7 +31,7 @@ const lenPath = len("/view/") func viewHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[lenPath:] p, _ := loadPage(title) - fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.title, p.body) + fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body) } func main() { diff --git a/doc/codelab/wiki/srcextract.go b/doc/codelab/wiki/srcextract.go index 0addc61c4..cab092f58 100644 --- a/doc/codelab/wiki/srcextract.go +++ b/doc/codelab/wiki/srcextract.go @@ -6,6 +6,7 @@ import ( "go/parser" "go/printer" "go/ast" + "go/token" "log" "os" ) @@ -25,9 +26,10 @@ func main() { os.Exit(2) } // load file - file, err := parser.ParseFile(*srcFn, nil, 0) + fs := token.NewFileSet() + file, err := parser.ParseFile(fs, *srcFn, nil, 0) if err != nil { - log.Exit(err) + log.Fatal(err) } // create printer p := &printer.Config{ @@ -47,7 +49,7 @@ func main() { os.Exit(1) } b := new(bytes.Buffer) - p.Fprint(b, file) + p.Fprint(b, fs, file) // drop package declaration if !*showPkg { for { diff --git a/doc/codelab/wiki/test.sh b/doc/codelab/wiki/test.sh index 5b752fe3c..95ff145b9 100755 --- a/doc/codelab/wiki/test.sh +++ b/doc/codelab/wiki/test.sh @@ -1,24 +1,27 @@ -#1/bin/bash - -./final.bin & -wiki_pid=$! +#!/usr/bin/env bash +set -e +wiki_pid= cleanup() { kill $wiki_pid - rm -f test_*.out Test.txt - exit ${1:-1} + rm -f test_*.out Test.txt final-test.bin final-test.go } -trap cleanup INT +trap cleanup 0 INT -sleep 1 +gomake get.bin +addr=$(./get.bin -addr) +sed s/:8080/$addr/ < final.go > final-test.go +gomake final-test.bin +./final-test.bin & +wiki_pid=$! -curl -s -o test_edit.out http://localhost:8080/edit/Test -cmp test_edit.out test_edit.good || cleanup 1 -curl -s -o /dev/null -d body=some%20content http://localhost:8080/save/Test -cmp Test.txt test_Test.txt.good || cleanup 1 -curl -s -o test_view.out http://localhost:8080/view/Test -cmp test_view.out test_view.good || cleanup 1 +sleep 1 -echo "Passed" -cleanup 0 +./get.bin http://$addr/edit/Test > test_edit.out +diff -u test_edit.out test_edit.good +./get.bin -post=body=some%20content http://$addr/save/Test +diff -u Test.txt test_Test.txt.good +./get.bin http://$addr/view/Test > test_view.out +diff -u test_view.out test_view.good +echo PASS diff --git a/doc/codelab/wiki/view.html b/doc/codelab/wiki/view.html index a46622d01..ca2ffc20b 100644 --- a/doc/codelab/wiki/view.html +++ b/doc/codelab/wiki/view.html @@ -1,5 +1,5 @@ -<h1>{title}</h1> +<h1>{Title}</h1> -<p>[<a href="/edit/{title}">edit</a>]</p> +<p>[<a href="/edit/{Title}">edit</a>]</p> -<div>{body}</div> +<div>{Body}</div> diff --git a/doc/codelab/wiki/wiki.html b/doc/codelab/wiki/wiki.html index 919385edf..ff2c3088b 100644 --- a/doc/codelab/wiki/wiki.html +++ b/doc/codelab/wiki/wiki.html @@ -71,27 +71,27 @@ declaration. <p> Let's start by defining the data structures. A wiki consists of a series of interconnected pages, each of which has a title and a body (the page content). -Here, we define <code>page</code> as a struct with two fields representing +Here, we define <code>Page</code> as a struct with two fields representing the title and body. </p> <pre> -!./srcextract.bin -src=part1.go -name=page +!./srcextract.bin -src=part1.go -name=Page </pre> <p> The type <code>[]byte</code> means "a <code>byte</code> slice". (See <a href="http://golang.org/doc/effective_go.html#slices">Effective Go</a> for more on slices.) -The <code>body</code> element is a <code>[]byte</code> rather than +The <code>Body</code> element is a <code>[]byte</code> rather than <code>string</code> because that is the type expected by the <code>io</code> libraries we will use, as you'll see below. </p> <p> -The <code>page</code> struct describes how page data will be stored in memory. +The <code>Page</code> struct describes how page data will be stored in memory. But what about persistent storage? We can address that by creating a -<code>save</code> method on <code>page</code>: +<code>save</code> method on <code>Page</code>: </p> <pre> @@ -100,13 +100,13 @@ But what about persistent storage? We can address that by creating a <p> This method's signature reads: "This is a method named <code>save</code> that -takes as its receiver <code>p</code>, a pointer to <code>page</code> . It takes +takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes no parameters, and returns a value of type <code>os.Error</code>." </p> <p> -This method will save the <code>page</code>'s <code>body</code> to a text -file. For simplicity, we will use the <code>title</code> as the file name. +This method will save the <code>Page</code>'s <code>Body</code> to a text +file. For simplicity, we will use the <code>Title</code> as the file name. </p> <p> @@ -114,7 +114,7 @@ The <code>save</code> method returns an <code>os.Error</code> value because that is the return type of <code>WriteFile</code> (a standard library function that writes a byte slice to a file). The <code>save</code> method returns the error value, to let the application handle it should anything go wrong while -writing the file. If all goes well, <code>page.save()</code> will return +writing the file. If all goes well, <code>Page.save()</code> will return <code>nil</code> (the zero-value for pointers, interfaces, and some other types). </p> @@ -136,8 +136,8 @@ We will want to load pages, too: <p> The function <code>loadPage</code> constructs the file name from -<code>title</code>, reads the file's contents into a new -<code>page</code>, and returns a pointer to that new <code>page</code>. +<code>Title</code>, reads the file's contents into a new +<code>Page</code>, and returns a pointer to that new <code>page</code>. </p> <p> @@ -151,7 +151,7 @@ error return value (in essence, assigning the value to nothing). <p> But what happens if <code>ReadFile</code> encounters an error? For example, the file might not exist. We should not ignore such errors. Let's modify the -function to return <code>*page</code> and <code>os.Error</code>. +function to return <code>*Page</code> and <code>os.Error</code>. </p> <pre> @@ -160,7 +160,7 @@ function to return <code>*page</code> and <code>os.Error</code>. <p> Callers of this function can now check the second parameter; if it is -<code>nil</code> then it has successfully loaded a page. If not, it will be an +<code>nil</code> then it has successfully loaded a Page. If not, it will be an <code>os.Error</code> that can be handled by the caller (see the <a href="http://golang.org/pkg/os/#Error">os package documentation</a> for details). @@ -179,7 +179,7 @@ written: <p> After compiling and executing this code, a file named <code>TestPage.txt</code> would be created, containing the contents of <code>p1</code>. The file would -then be read into the struct <code>p2</code>, and its <code>body</code> element +then be read into the struct <code>p2</code>, and its <code>Body</code> element printed to the screen. </p> @@ -334,7 +334,7 @@ href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code should show a page titled "test" containing the words "Hello world". </p> -<h2>Editing pages</h2> +<h2>Editing Pages</h2> <p> A wiki is not a wiki without the ability to edit pages. Let's create two new @@ -353,7 +353,7 @@ First, we add them to <code>main()</code>: <p> The function <code>editHandler</code> loads the page -(or, if it doesn't exist, create an empty <code>page</code> struct), +(or, if it doesn't exist, create an empty <code>Page</code> struct), and displays an HTML form. </p> @@ -413,15 +413,15 @@ The function <code>template.ParseFile</code> will read the contents of <p> The method <code>t.Execute</code> replaces all occurrences of -<code>{title}</code> and <code>{body}</code> with the values of -<code>p.title</code> and <code>p.body</code>, and writes the resultant +<code>{Title}</code> and <code>{Body}</code> with the values of +<code>p.Title</code> and <code>p.Body</code>, and writes the resultant HTML to the <code>http.ResponseWriter</code>. </p> <p> -Note that we've used <code>{body|html}</code> in the above template. +Note that we've used <code>{Body|html}</code> in the above template. The <code>|html</code> part asks the template engine to pass the value -<code>body</code> through the <code>html</code> formatter before outputting it, +<code>Body</code> through the <code>html</code> formatter before outputting it, which escapes HTML characters (such as replacing <code>></code> with <code>&gt;</code>). This will prevent user data from corrupting the form HTML. @@ -472,8 +472,8 @@ The handlers are now shorter and simpler. <p> What if you visit <code>/view/APageThatDoesntExist</code>? The program will crash. This is because it ignores the error return value from -<code>loadPage</code>. Instead, if the requested page doesn't exist, it should -redirect the client to the edit page so the content may be created: +<code>loadPage</code>. Instead, if the requested Page doesn't exist, it should +redirect the client to the edit Page so the content may be created: </p> <pre> @@ -486,7 +486,7 @@ The <code>http.Redirect</code> function adds an HTTP status code of header to the HTTP response. </p> -<h2>Saving pages</h2> +<h2>Saving Pages</h2> <p> The function <code>saveHandler</code> will handle the form submission. @@ -498,7 +498,7 @@ The function <code>saveHandler</code> will handle the form submission. <p> The page title (provided in the URL) and the form's only field, -<code>body</code>, are stored in a new <code>page</code>. +<code>Body</code>, are stored in a new <code>Page</code>. The <code>save()</code> method is then called to write the data to a file, and the client is redirected to the <code>/view/</code> page. </p> @@ -506,7 +506,7 @@ and the client is redirected to the <code>/view/</code> page. <p> The value returned by <code>FormValue</code> is of type <code>string</code>. We must convert that value to <code>[]byte</code> before it will fit into -the <code>page</code> struct. We use <code>[]byte(body)</code> to perform +the <code>Page</code> struct. We use <code>[]byte(body)</code> to perform the conversion. </p> @@ -610,7 +610,7 @@ Then we can create a global variable to store our validation regexp: </p> <pre> -!./srcextract.bin -src=final-noclosure.go -name=titleValidator +!./srcextract.bin -src=final-noclosure.go -name=TitleValidator </pre> <p> @@ -624,7 +624,7 @@ the expression compilation fails, while <code>Compile</code> returns an <p> Now, let's write a function that extracts the title string from the request -URL, and tests it against our <code>titleValidator</code> expression: +URL, and tests it against our <code>TitleValidator</code> expression: </p> <pre> @@ -708,7 +708,7 @@ The closure returned by <code>makeHandler</code> is a function that takes an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other words, an <code>http.HandlerFunc</code>). The closure extracts the <code>title</code> from the request path, and -validates it with the <code>titleValidator</code> regexp. If the +validates it with the <code>TitleValidator</code> regexp. If the <code>title</code> is invalid, an error will be written to the <code>ResponseWriter</code> using the <code>http.NotFound</code> function. If the <code>title</code> is valid, the enclosed handler function |