| Inside the "present" tool |
| |
| Andrew Gerrand |
| Google |
| @enneff |
| adg@golang.org |
| http://golang.org |
| |
| |
| * The Playground API |
| |
| The API used by the Playground (and the Tour) is a simple HTTP POST request |
| that returns a JSON-encoded response. |
| |
| Request: |
| |
| POST /compile HTTP/1.1 |
| Host:play.golang.org |
| Content-Length:113 |
| Content-Type:application/x-www-form-urlencoded; charset=UTF-8 |
| |
| body=package+main%0A%0Aimport+%22fmt%22%0A%0Afunc+main()+%7B%0A%09fmt.Println(%22Hello%2C+playground%22)%0A%7D%0A |
| |
| Response body: |
| |
| {"compile_errors":"","output":"Hello, playground\n"} |
| |
| |
| * Playground drawbacks |
| |
| The compile service has no concept of time. (Necessary to limit resource use.) |
| |
| The API reflects this; output is sent in one blob, not streamed. |
| |
| Even when running locally, the API is bad for demonstrating code that uses time. |
| |
| Rob needed to use time in his _Go_Concurrency_Patterns_ talk. |
| |
| |
| * Enter WebSockets |
| |
| WebSockets are a bi-directional communication channel between a JavaScript program running in a web browser and a web server. They are part of HTML 5. |
| |
| The `websocket` package in Go's `go.net` sub-repository provides a WebSocket client and server. |
| |
| I thought I could use WebSockets to stream program output to a running |
| presentation. |
| |
| And thus the `present` tool was born. |
| |
| |
| * Hello, WebSocket |
| |
| .code insidepresent/websocket.js |
| .play insidepresent/websocket.go |
| |
| |
| * Messages |
| |
| The client (browser) and server (present) communicate with JSON-encoded messages. |
| |
| .code insidepresent/socket.go /Message is/,/^}/ |
| |
| Go's `encoding/json` format can convert these `Message` values to and from JSON. |
| |
| Go: |
| |
| Message{Id: "0", Kind: "run", Body: `package main; func main() { print("hello"); }`} |
| |
| JSON: |
| |
| {"Id":"0","Kind":"run","Body":"package main; func main() { print(\"hello\"); }"} |
| |
| |
| * On the wire |
| |
| .play insidepresent/hello.go |
| |
| .html insidepresent/wire.html |
| |
| |
| * Implementation |
| |
| * socketHandler (1/3) |
| |
| First, register the handler with the `net/http` package: |
| |
| http.Handle("/socket", websocket.Handler(socketHandler)) |
| |
| Implementation: |
| |
| .code insidepresent/socket.go /func socketHandler/,/errc/ |
| |
| * socketHandler (2/3) |
| |
| .code insidepresent/socket.go /Decode messages/,/END/ |
| |
| * socketHandler (3/3) |
| |
| .code insidepresent/socket-simple.go /Start and kill/,/^}/ |
| |
| |
| * Process |
| |
| .code insidepresent/socket.go /Process represents/,/^}/ |
| |
| * StartProcess |
| |
| .code insidepresent/socket.go /StartProcess builds/,/^}/ |
| |
| * Process.start (1/2) |
| |
| .code insidepresent/socket.go /start builds/,/END/ |
| |
| * Process.start (2/2) |
| |
| .code insidepresent/socket.go /build x\.go/,/^}/ |
| |
| * Process.cmd |
| |
| .code insidepresent/socket.go /cmd builds/,/^}/ |
| .code insidepresent/socket.go /messageWriter is/,/END/ |
| |
| * Process.wait and Process.end |
| |
| .code insidepresent/socket.go /wait waits/,/^}/ |
| .code insidepresent/socket.go /end sends/,/^}/ |
| |
| * Process.Kill |
| |
| .code insidepresent/socket.go /Kill stops/,/^}/ |
| |
| |
| * One more thing |
| |
| * Limiting output (1/2) |
| |
| .code insidepresent/socket.go /switch m\.Kind/,/^ }/ |
| |
| * Limiting output (2/2) |
| |
| .code insidepresent/socket.go /limiter returns/,/^}/ |
| |
| |