refactoring
2 files changed, 188 insertions(+), 169 deletions(-)

M metronome.go
M server.go
M metronome.go +37 -8
@@ 1,5 1,8 @@ 
 package main
 
+import "fmt"
+import "os"
+import "io"
 import "io/ioutil"
 
 const (

          
@@ 8,10 11,9 @@ const (
     EventDelete         = "delete"
 )
 
-type EntityPath struct {
-    WebPath         string
-    FSPath          string
-}
+var (
+    ResourceNotFound    = fmt.Errorf("Required resource was not found")
+)
 
 type Request struct {
     Subscribe       bool

          
@@ 30,18 32,18 @@ type State struct {
 }
 
 type DelayedCachedReader struct {
-    Path            string
+    File            io.Reader
     IsLoaded        bool
     data            []byte
 }
 
-func NewDelayedCachedReader(path string) *DelayedCachedReader {
-    return &DelayedCachedReader{path, false, nil}
+func NewDelayedCachedReader(r io.Reader) *DelayedCachedReader {
+    return &DelayedCachedReader{r, false, nil}
 }
 
 func (r *DelayedCachedReader) MarshalJSON() ([]byte, error) {
     if r.IsLoaded == false {
-        data, err := ioutil.ReadFile(r.Path)
+        data, err := ioutil.ReadAll(r.File)
         if err != nil {
             return nil, err
         }

          
@@ 50,3 52,30 @@ func (r *DelayedCachedReader) MarshalJSO
     }
     return r.data, nil
 }
+
+type Resource struct {
+    WebPath         string
+    FSPath          string
+}
+
+type OpenResource struct {
+    *Resource
+    *os.File
+    os.FileInfo
+}
+
+func (r *OpenResource) Payload() (interface{}, error) {
+    if r.IsDir() {
+        fis, err := r.Readdir(0)
+        if err != nil {
+            return nil, err
+        }
+        payload := make([]string, len(fis))
+        for i, fi := range fis {
+            payload[i] = fi.Name()
+        }
+        return payload, nil
+    } else {
+        return NewDelayedCachedReader(r), nil
+    }
+}

          
M server.go +151 -161
@@ 3,7 3,6 @@ package main
 import "net/http"
 import "os"
 import "log"
-import "io/ioutil"
 import "fmt"
 import "strings"
 import "path/filepath"

          
@@ 50,79 49,45 @@ func NewMetronomeHandler(watchdir string
     return h, nil
 }
 
-func (h *MetronomeHandler) toFsPath(path string) (string, error) {
-    path, err := filepath.Abs(filepath.Join(h.watchdir, path))
-    if err != nil {
-        return "", err
+func (h *MetronomeHandler) resFromWebPath(path string) (*Resource, error) {
+    if path == "" {
+        return nil, fmt.Errorf("Path must not be empty")
     }
-    //todo filepath.EvalSymlinks?
-    if strings.HasPrefix(path, h.watchdir) == false {
-        return "", fmt.Errorf("Path does not appear to be inside a valid directory")
-    }
-    return path, nil
-}
-
-func dirListing(fspath string) ([]string, error) {
-    listing := make([]string, 0)
-    fis, err := ioutil.ReadDir(fspath)
+    fspath, err := filepath.Abs(filepath.Join(h.watchdir, path))
     if err != nil {
         return nil, err
-    } else {
-        for _, fi := range fis {
-            item := fi.Name()
-            log.Println("listing", item)
-            if fi.IsDir() {
-                item = givePathSuffix(item)
-            }
-            listing = append(listing, item)
-        }
     }
-    return listing, nil
+    //fspath, err = filepath.EvalSymlinks(fspath)
+    //if err != nil {
+    //    return nil, err
+    //}
+    return &Resource{path, fspath}, nil
 }
 
-func (h *MetronomeHandler) serveWS(ws *websocket.Conn) {
-    defer ws.Close()
-    for {
-        log.Println("serving:", ws)
-        if err := h.readWS(ws); err != nil {
-            log.Println("serveWS error:", err)
-            break
-        }
+func (h *MetronomeHandler) openResource(r *Resource) (*OpenResource, error) {
+    file, err := os.Open(r.FSPath)
+    if err != nil {
+        return nil,err
     }
+    fi, err := file.Stat()
+    if err != nil {
+        return nil, err
+    }
+    return &OpenResource{r, file, fi}, nil
 }
 
-func (h *MetronomeHandler) readWS(ws *websocket.Conn) error {
-    r := new(Request)
-    if err := websocket.JSON.Receive(ws, r); err != nil {
-        return websocket.JSON.Send(ws, &Error{
-            Message: "Invalid request",
-            Details: err.Error(),
-        })
-    }
-    log.Println(r)
-    if r.Path == "" {
-        return websocket.JSON.Send(ws, &Error{
-            Message: "Invalid path in request",
-            Details: "Path must not be empty",
-        })
+func (h *MetronomeHandler) validateResource(r *Resource) error {
+    if strings.HasPrefix(r.FSPath, h.watchdir) == false {
+        return fmt.Errorf("Path does not appear to be inside a valid directory")
     }
-    fspath, err := h.toFsPath(r.Path)
-    if err != nil {
-        return websocket.JSON.Send(ws, &Error{
-            Message: "Invalid path in request",
-            Details: err.Error(),
-        })
-    }
-    subs, ok := h.subs[r.Path]
-    if ok == false {
-        subs = make([]*websocket.Conn, 0)
-    }
-    subs = append(subs, ws)
-    h.subs[r.Path] = subs
+    return nil
+}
 
+/*
+func (h *MetronomeHandler) resourceData(r *Resource) (interface{}, error) {
     // Get Directory listing
-    if r.Path[len(r.Path)-1] == os.PathSeparator {
-        listing, err := dirListing(fspath)
+    if r.IsDir() {
+        listing, err := r.dirListing(fspath)
         if err != nil {
             log.Printf("error getting listing of %v: %v", fspath, err)
             return websocket.JSON.Send(ws, &Error{

          
@@ 142,33 107,93 @@ func (h *MetronomeHandler) readWS(ws *we
         Payload: NewDelayedCachedReader(fspath),
     })
 }
+*/
+
+func (h *MetronomeHandler) serveWS(ws *websocket.Conn) {
+    defer ws.Close()
+    for {
+        log.Println("serving:", ws)
+        if err := h.serveOneWS(ws); err != nil {
+            log.Println("serveWS error:", err)
+            break
+        }
+    }
+}
+
+func (h *MetronomeHandler) serveOneWS(ws *websocket.Conn) error {
+    r := new(Request)
+    if err := websocket.JSON.Receive(ws, r); err != nil {
+        return websocket.JSON.Send(ws, &Error{
+            Message: "Invalid request",
+            Details: err.Error(),
+        })
+    }
+
+    log.Println("request:", r)
+
+    res, err := h.resFromWebPath(r.Path)
+    if err == nil {
+        err = h.validateResource(res)
+    }
+    if err != nil {
+        return websocket.JSON.Send(ws, &Error{
+            Message: "Invalid path in request",
+            Details: err.Error(),
+        })
+    }
+
+    // Open the resource
+    ores, err := h.openResource(res)
+    if err != nil {
+        return websocket.JSON.Send(ws, &Error{
+            Message: "Could not read resource",
+            Details: err.Error(),
+        })
+    }
+    payload, err := ores.Payload()
+    if err != nil {
+        return websocket.JSON.Send(ws, &Error{
+            Message: "Could not read resource",
+            Details: err.Error(),
+        })
+    }
+
+    // Subscribe to the resource
+    subs, ok := h.subs[res.FSPath]
+    if ok == false {
+        subs = make([]*websocket.Conn, 0)
+    }
+    subs = append(subs, ws)
+    h.subs[res.WebPath] = subs
+
+    // Send the resource
+    return websocket.JSON.Send(ws, &State{
+        Path: res.WebPath,
+        Payload: payload,
+    })
+}
 
 func (h *MetronomeHandler) handleFsEvents() {
     for {
+        log.Println("waiting for fs events")
         select {
         case ev := <-h.watcher.Event:
+            var err error
             path := ev.Name[len(h.watchdir):]
             log.Println("event:", ev, path)
-            if ev.IsDelete() || ev.IsRename() {
-                if path[len(path)-1] == os.PathSeparator {
-                    h.dirRemoved(path)
-                } else {
-                    // Not sure if it's a directory or a file I guess
-                    h.dirRemoved(path)
-                    h.fileRemoved(path)
+            res, err := h.resFromWebPath(path)
+            if err != nil {
+                log.Println("error resolving resource in handling fs event:", err)
+            } else {
+                if ev.IsDelete() || ev.IsRename() {
+                    err = h.resRemoved(res)
+                } else if ev.IsModify() {
+                    err = h.resModified(res)
+                } else if ev.IsCreate() {
+                    err = h.resAdded(res)
                 }
-            } else if ev.IsModify() {
-                    h.fileModified(path)
-            } else if ev.IsCreate() {
-                fi, err := os.Stat(ev.Name)
                 if err != nil {
-                    log.Printf("Failure getting stat of %v: %v\n", path, err)
-                    break
-                }
-                if fi.IsDir() {
-                    h.dirAdded(path)
-                } else {
-                    h.fileAdded(path)
+                    log.Println("error dealing with resource:", err)
                 }
             }
         case err := <-h.watcher.Error:

          
@@ 186,102 211,67 @@ func (h *MetronomeHandler) notifyConns(p
     conns, ok := h.subs[path]
     if ok {
         for _, conn := range conns {
-            if err := notifyConn(conn, msg); err != nil {
-                log.Println("Error while notifying a connection", err)
-            }
+            go func(conn *websocket.Conn) {
+                if err := notifyConn(conn, msg); err != nil {
+                    log.Println("Error while notifying a connection:", err)
+                }
+            }(conn)
         }
     }
 }
 
-func (h *MetronomeHandler) fileAdded(path string) {
-    fspath, err := h.toFsPath(path)
+func (h *MetronomeHandler) resAdded(res *Resource) error {
+    ores, err := h.openResource(res)
     if err != nil {
-        panic(err)
+        return err
     }
-    h.notifyConns(path, &State{
-        Event: EventCreate,
-        Path: path,
-        Payload: NewDelayedCachedReader(fspath),
-    })
-    h.dirModified(filepath.Dir(path));
-}
-
-func (h *MetronomeHandler) fileModified(path string) {
-    fspath, err := h.toFsPath(path)
+    payload, err := ores.Payload()
     if err != nil {
-        panic(err)
+        return err
     }
-    h.notifyConns(path, &State{
-        Event: EventDelete,
-        Path: path,
-        Payload: NewDelayedCachedReader(fspath),
+    h.notifyConns(res.WebPath, &State{
+        Event: EventCreate,
+        Path: res.WebPath,
+        Payload: payload,
     })
-}
-
-func (h *MetronomeHandler) fileRemoved(path string) {
-    h.notifyConns(path, &State{
-        Event: EventDelete,
-        Path: path,
-    })
-    h.dirModified(filepath.Dir(path));
+    if res.WebPath != "/" {
+        parent, err := h.resFromWebPath(filepath.Dir(res.WebPath))
+        if err != nil {
+            return err
+        }
+        return h.resModified(parent)
+    }
+    return nil
 }
 
-func (h *MetronomeHandler) dirAdded(path string) {
-    fspath, err := h.toFsPath(path)
+func (h *MetronomeHandler) resModified(res *Resource) error {
+    ores, err := h.openResource(res)
     if err != nil {
-        panic(err)
+        return err
     }
-    path = givePathSuffix(path)
-    h.notifyConns(path, &State{
-        Event: EventCreate,
-        Path: path,
+    payload, err := ores.Payload()
+    if err != nil {
+        return err
+    }
+    h.notifyConns(res.WebPath, &State{
+        Event: EventModify,
+        Path: res.WebPath,
+        Payload: payload,
     })
-    filepath.Walk(fspath, func(path string, info os.FileInfo, err error) error {
-        log.Println("walking", path)
-        if err != nil {
-            log.Println("error while walking into new directory: ", err)
-        } else {
-            if info.IsDir() {
-                h.dirAdded(path)
-            } else {
-                h.fileAdded(path)
-            }
-        }
-        return nil
-    })
+    return nil
 }
 
-func (h *MetronomeHandler) dirModified(path string) {
-    fspath, err := h.toFsPath(path)
-    if err != nil {
-        panic(err)
+func (h *MetronomeHandler) resRemoved(res *Resource) error {
+    h.notifyConns(res.WebPath, &State{
+        Event: EventDelete,
+        Path: res.WebPath,
+    })
+    if res.WebPath != "/" {
+        parent, err := h.resFromWebPath(filepath.Dir(res.WebPath))
+        if err != nil {
+            return err
+        }
+        return h.resModified(parent)
     }
-    path = givePathSuffix(path)
-    listing, err := dirListing(fspath)
-    if err != nil {
-        log.Printf("error getting listing of %v: %v", path, err)
-        return
-    }
-    h.notifyConns(path, &State{
-        Event: EventDelete,
-        Path: path,
-        Payload: listing,
-    })
+    return nil
 }
-
-func (h *MetronomeHandler) dirRemoved(path string) {
-    path = givePathSuffix(path)
-    for name, _ := range h.subs {
-        if strings.HasPrefix(name, path) {
-            if name[len(name)-1] == os.PathSeparator {
-                h.dirRemoved(name)
-            } else {
-                h.fileRemoved(name)
-            }
-        }
-    }
-    h.notifyConns(path, &State{
-        Event: EventDelete,
-        Path: path,
-    })
-}