Externalizing web assets

mostly to ease editing, but also for practice with file manipulation,
gzipping, base64'ing, etc.
This commit is contained in:
Dan Buch 2012-12-18 21:56:37 -05:00
parent 4c44252a49
commit f86855d22d
8 changed files with 230 additions and 1871 deletions

1
conway/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/web_assets/web_assets

View File

@ -4,18 +4,21 @@ TARGETS := \
$(LIBS) \
github.com/meatballhat/box-o-sand/conway/conways-game-of-life
all: deps build
all: test
build: test
test: build
go test -x -v -test.parallel=4 $(LIBS)
build: web_assets.go deps
go install -x $(TARGETS)
web_assets.go: $(wildcard web_assets/*.*)
./gen-web-assets
clean:
go clean -x -i $(TARGETS)
test:
go test -x -v -test.parallel=4 $(LIBS)
deps:
go get -x $(TARGETS)
go get -n -x $(TARGETS)
.PHONY: all build clean deps test

View File

@ -477,7 +477,7 @@ func TestHandleWebGameRootReturnsIndexPageForGET(t *testing.T) {
HandleWebGameRoot(w, req)
if w.Status != http.StatusOK {
t.Fail()
t.Errorf("response status != %d: %d", http.StatusOK, w.Status)
return
}
@ -488,7 +488,7 @@ func TestHandleWebGameRootReturnsIndexPageForGET(t *testing.T) {
}
if !matched {
t.Fail()
t.Errorf("Body did not match expected <h1> element")
return
}
}

6
conway/gen-web-assets Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -e
cd web_assets
go build .
./web_assets index.html | gofmt > ../web_assets.go

View File

@ -2,6 +2,7 @@ package conway
import (
"bytes"
"compress/gzip"
"encoding/base64"
"encoding/json"
"errors"
@ -28,15 +29,56 @@ type WebGameState struct {
Img string `json:"i"`
}
var jQueryMinJs = []byte("")
type webAssetStrings struct {
IndexHTML []byte
NormalizeCSS []byte
JqueryMinJS []byte
}
var WebAssets webAssetStrings
func init() {
rand.Seed(time.Now().UTC().UnixNano())
jqmin, err := base64.StdEncoding.DecodeString(JQUERY_MIN_JS)
jqmin, err := fromGzB64(JQUERY_MIN_JS)
if err != nil {
log.Fatal("Failed to decode internal jquery.min.js")
}
jQueryMinJs = append(jQueryMinJs, jqmin...)
WebAssets.JqueryMinJS = jqmin
normCss, err := fromGzB64(NORMALIZE_CSS)
if err != nil {
log.Fatal("Failed to decode internal normalize.css")
}
WebAssets.NormalizeCSS = normCss
idxHt, err := fromGzB64(GAME_OF_LIFE_INDEX_HTML)
if err != nil {
log.Fatal("Failed to decode internal index.html")
}
WebAssets.IndexHTML = idxHt
}
func fromGzB64(input string) ([]byte, error) {
decoded, err := base64.StdEncoding.DecodeString(input)
if err != nil {
return []byte(""), err
}
buf := bytes.NewBuffer(decoded)
zReader, err := gzip.NewReader(buf)
if err != nil {
return []byte(""), err
}
defer zReader.Close()
gunzipped, err := ioutil.ReadAll(zReader)
if err != nil {
return []byte(""), err
}
return gunzipped, nil
}
func NewWebGameParams(q url.Values) *WebGameParams {
@ -99,7 +141,7 @@ func HandleWebGameRoot(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
w.Write([]byte(GAME_OF_LIFE_INDEX_HTML))
w.Write(WebAssets.IndexHTML)
return
}
@ -112,14 +154,14 @@ func HandleWebGameStatic(w http.ResponseWriter, req *http.Request) {
if req.URL.Path == "/static/normalize.css" {
w.Header().Set("Content-Type", "text/css")
w.WriteHeader(http.StatusOK)
w.Write([]byte(NORMALIZE_CSS))
w.Write(WebAssets.NormalizeCSS)
return
}
if req.URL.Path == "/static/jquery.min.js" {
w.Header().Set("Content-Type", "text/javascript")
w.WriteHeader(http.StatusOK)
w.Write(jQueryMinJs)
w.Write(WebAssets.JqueryMinJS)
return
}

File diff suppressed because one or more lines are too long

110
conway/web_assets/gen.go Normal file
View File

@ -0,0 +1,110 @@
package main
import (
"bufio"
"bytes"
"compress/gzip"
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"text/template"
"time"
)
var (
webAssetsTmpl = template.Must(template.New("web_assets").Parse("" +
"package conway\n" +
"\n" +
"const (\n" +
" GAME_OF_LIFE_INDEX_HTML = `{{.IndexHTML}}`\n" +
" NORMALIZE_CSS = `{{.NormalizeCSS}}`\n" +
" JQUERY_MIN_JS = `{{.JqueryMinJS}}`\n" +
")\n"))
normalizeCssUrl = "http://necolas.github.com/normalize.css/2.0.1/normalize.css"
jqueryMinJsUrl = "http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"
)
type webAssetStrings struct {
IndexHTML string
NormalizeCSS string
JqueryMinJS string
Now time.Time
}
func die(err error) {
log.Fatal("CRUD:", err)
}
func toB64(input []byte) string {
return base64.StdEncoding.EncodeToString(input)
}
func toGz(input []byte) []byte {
var buf bytes.Buffer
zwriter := gzip.NewWriter(&buf)
zbuf := bufio.NewWriter(zwriter)
toWrite := len(input)
for written := 0; written < toWrite; {
n, err := zbuf.Write(input[written:])
if err != nil {
die(err)
}
written += n
}
zbuf.Flush()
zwriter.Close()
return buf.Bytes()
}
func fetchUrl(url string) []byte {
resp, err := http.Get(url)
if err != nil {
die(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
die(err)
}
return body
}
func fetchIndexHtml(indexHtmlPath string) []byte {
fd, err := os.Open(indexHtmlPath)
if err != nil {
die(err)
}
body, err := ioutil.ReadAll(fd)
if err != nil {
die(err)
}
return body
}
func main() {
if len(os.Args) < 2 {
die(errors.New(fmt.Sprintf("Usage: %s <index.html-path>", os.Args[0])))
}
assets := &webAssetStrings{
IndexHTML: toB64(toGz(fetchIndexHtml(os.Args[1]))),
NormalizeCSS: toB64(toGz(fetchUrl(normalizeCssUrl))),
JqueryMinJS: toB64(toGz(fetchUrl(jqueryMinJsUrl))),
Now: time.Now(),
}
webAssetsTmpl.Execute(os.Stdout, assets)
}

View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html>
<head>
<title>Conway's Game of Life</title>
<link type="text/css" rel="stylesheet" href="/static/normalize.css" />
<script type="text/javascript" src="/static/jquery.min.js"></script>
<script type="text/javascript">
curState = {};
curImg = "";
function populateInitialState() {
$.getJSON('/state', {}, onPopulateResponse);
}
function goToNextState(state) {
$.post('/state', JSON.stringify({s: state}, null, 2), onPopulateResponse, 'json');
}
function playGame() {
goToNextState(curState);
setTimeout(playGame, 500);
}
function onPopulateResponse(data, textStatus, jqXHR) {
$.extend(curState, data.s);
curImg = data.i;
var $stateImg = $('#state_img');
if ($stateImg.length < 1) {
$stateImg = $('<img id="state_img" />').appendTo($('#state_container'));
}
$stateImg.attr('src', 'data:image/png;base64,' + encodeURI(data.i));
}
$(function() {
populateInitialState();
$('#step').click(function() { goToNextState(curState); });
$('#play').click(playGame);
});
</script>
</head>
<body>
<h1>Conway's Game of Life</h1>
<div id="controls">
<button id="step">Step</button>
<button id="play">Play</button>
</div>
<hr />
<div id="state_container">
</div>
</body>
</html>