working on adding http support
parent
55e08628c0
commit
7008e3d140
@ -0,0 +1,114 @@
|
||||
package am
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// an asset manager is responsible for managing references to files contained
|
||||
// in source-controlled directories that associate to Go packages. I suspect
|
||||
// that there's some overlap here with the standard library go/build package.
|
||||
type Manager struct {
|
||||
basePath string // base path on the local filesystem
|
||||
baseURLPath string // base path for public-facing URLS
|
||||
}
|
||||
|
||||
// given an import path for a Go package, builds an asset manager for that
|
||||
// package. Doesn't actually check if that package exists; this is kinda
|
||||
// hazardous right now. ¯\_(ツ)_/¯
|
||||
func New(importPath, urlBase string) *Manager {
|
||||
return &Manager{basePath: pkgDir(importPath), baseURLPath: urlBase}
|
||||
}
|
||||
|
||||
func (m *Manager) AbsPath(parts ...string) string {
|
||||
return filepath.Join(m.basePath, filepath.Join(parts...))
|
||||
}
|
||||
|
||||
func (m *Manager) Open(parts ...string) (*os.File, error) {
|
||||
return os.Open(m.AbsPath(parts...))
|
||||
}
|
||||
|
||||
func (m *Manager) URLPath(parts ...string) string {
|
||||
return path.Join(m.baseURLPath, path.Join(parts...))
|
||||
}
|
||||
|
||||
func (m *Manager) ReadFile(parts ...string) ([]byte, error) {
|
||||
f, err := m.Open(parts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return ioutil.ReadAll(f)
|
||||
}
|
||||
|
||||
// getPathDirs gets the user's GOPATH environment variable and splits it along
|
||||
// the OS-specific path separator, returning a slice of strings, one per path
|
||||
// directory.
|
||||
func getPathDirs() []string {
|
||||
path := os.Getenv("GOPATH")
|
||||
if path == "" {
|
||||
panic("am: GOPATH not set")
|
||||
}
|
||||
return strings.Split(path, string(os.PathListSeparator))
|
||||
}
|
||||
|
||||
// getPkgDirCandidates gets the list of possible locations for a given import
|
||||
// string. The paths are not guaranteed to exist or to even be valid; results
|
||||
// are derived from the user's $GOPATH environment variable, which is not
|
||||
// necessarily clean.
|
||||
func getPkgDirCandidates(importPath string) []string {
|
||||
pathDirs := getPathDirs()
|
||||
importParts := strings.Split(importPath, "/")
|
||||
candidates := make([]string, 0, len(pathDirs))
|
||||
for _, pathDir := range pathDirs {
|
||||
srcDir := filepath.Join(pathDir, "src")
|
||||
candidates = append(candidates, filepath.Join(srcDir, filepath.Join(importParts...)))
|
||||
}
|
||||
return candidates
|
||||
}
|
||||
|
||||
// existingDirectories accepts a slice of strings and returns a slice of
|
||||
// strings representing which of the given strings corresponds to an existing
|
||||
// directory. Note that this is prone to timing attacks, but it is presumed to
|
||||
// not matter for this application; this may be an unsafe strategy to copy into
|
||||
// other projects.
|
||||
func existingDirectories(candidates []string) []string {
|
||||
dirs := make([]string, 0, len(candidates))
|
||||
for _, path := range candidates {
|
||||
if isDir(path) {
|
||||
dirs = append(dirs, path)
|
||||
}
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
// isDir takes a file path and returns a boolean representing whether the path
|
||||
// is or is not a valid directory. Again, this is vulnerable to timing attacks
|
||||
// and should be considered a Very Bad Idea in most contexts.
|
||||
func isDir(path string) bool {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fi.IsDir()
|
||||
}
|
||||
|
||||
// getPkgDir takes an import path and returns a string representing the
|
||||
// directory path of the package on the current machine. This is generally a
|
||||
// stupid thing to do, because we're generally not worried about the location
|
||||
// of a package's source code files, since they may be out of sync with the
|
||||
// actual binary, but in our case, we're using it to look up assets that have
|
||||
// been made go-gettable. If no package dir can be found, an empty string is
|
||||
// returned. The package may reasonably be installed into multiple workspaces.
|
||||
// In this case, it's the first package found, as dictated by the user's
|
||||
// $GOPATH environment variable.
|
||||
func pkgDir(importPath string) string {
|
||||
dirs := existingDirectories(getPkgDirCandidates(importPath))
|
||||
if len(dirs) == 0 {
|
||||
return ""
|
||||
}
|
||||
return dirs[0]
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.net/websocket"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jordanorelli/skeam/am"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var assets = am.New("github.com/jordanorelli/skeam", "/static")
|
||||
|
||||
func templatePath(relpath string) string {
|
||||
return filepath.Join("templates", relpath)
|
||||
}
|
||||
|
||||
func getTemplate(relpath string) (*template.Template, error) {
|
||||
p := templatePath(relpath)
|
||||
b, err := assets.ReadFile(p)
|
||||
if err != nil {
|
||||
return nil, errors.New("skeam: unable to read template " + relpath)
|
||||
}
|
||||
|
||||
t := template.New(relpath).Funcs(template.FuncMap{
|
||||
"js": func(relpath string) (template.HTML, error) {
|
||||
jspath := assets.URLPath("js", relpath)
|
||||
return template.HTML(fmt.Sprintf(`<script src="%s"></script>`, jspath)), nil
|
||||
},
|
||||
"css": func(relpath string) (template.HTML, error) {
|
||||
csspath := assets.URLPath("css", relpath)
|
||||
return template.HTML(fmt.Sprintf(`<link type="text/css" rel="stylesheet" href="%s" />`, csspath)), nil
|
||||
},
|
||||
})
|
||||
t, err = t.Parse(string(b))
|
||||
if err != nil {
|
||||
return nil, errors.New("skeam: unable to parse template " + relpath + ": " + err.Error())
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
|
||||
type templateHandler string
|
||||
|
||||
func (t templateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
tpl, err := getTemplate(string(t))
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := tpl.Execute(w, nil); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func wsHandler(ws *websocket.Conn) {
|
||||
io.Copy(ws, ws)
|
||||
}
|
||||
|
||||
func runHTTPServer() {
|
||||
http.Handle("/", templateHandler("home.html"))
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(assets.AbsPath("static")))))
|
||||
http.Handle("/ws", websocket.Handler(wsHandler))
|
||||
http.ListenAndServe(*httpAddr, nil)
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#bodywrap {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
#txt-repl {
|
||||
position: relative;
|
||||
margin: -120px 0 0 0;
|
||||
height: 110px;
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
@ -0,0 +1 @@
|
||||
console.log("oh hai");
|
@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
{{css "home.css"}}
|
||||
</head>
|
||||
<body>
|
||||
<div id="bodywrap">
|
||||
</div>
|
||||
<div id="footer">
|
||||
<textarea id="txt-repl"></textarea>
|
||||
</div>
|
||||
{{js "skeam.js"}}
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue