Browse Source

Refactoring server for multiple session contexts

luis 7 years ago
parent
commit
0d914ebff5

+ 21 - 1
browser/vue-flow/src/App.vue

@@ -5,8 +5,27 @@
 </template>
 </template>
 
 
 <script>
 <script>
+import Vue from 'vue'
+import FlowService from './services/flowservice'
 export default {
 export default {
-  name: 'App'
+  name: 'App',
+  created () {
+    console.log(this.$route)
+    let ctx = this.$route.params.context
+    console.log('Context:', ctx)
+
+    let urlPath = [
+      window.location.host,
+      ctx,
+      'conn'
+    ]
+
+    let targetws = 'ws://' + urlPath.join('/')
+    if (window.location.protocol === 'https:') {
+      targetws = 'wss://' + urlPath.join('/')
+    }
+    Vue.use(FlowService, {location: targetws})
+  }
 }
 }
 </script>
 </script>
 
 
@@ -14,6 +33,7 @@ export default {
 * {
 * {
   box-sizing:border-box;
   box-sizing:border-box;
 }
 }
+
 #app {
 #app {
   position:relative;
   position:relative;
   padding:0;
   padding:0;

+ 2 - 2
browser/vue-flow/src/components/flow/manager.vue

@@ -308,12 +308,12 @@ export default {
           // find Parent
           // find Parent
 
 
           var curTarget = ev.target
           var curTarget = ev.target
-          for (;curTarget = curTarget.parentNode; curTarget !== document.body) {
+          for (; curTarget.hasAttribute !== undefined && curTarget !== document.body; curTarget = curTarget.parentNode) {
             if (curTarget.hasAttribute('data-nodeid')) {
             if (curTarget.hasAttribute('data-nodeid')) {
               break
               break
             }
             }
           }
           }
-          if (curTarget === document.body) {
+          if (!curTarget.hasAttribute || curTarget === document.body) {
             console.error('LINK: target is not a socket')
             console.error('LINK: target is not a socket')
             return
             return
           }
           }

+ 3 - 2
browser/vue-flow/src/components/main.vue

@@ -228,7 +228,7 @@ export default {
     // Handle incoming things
     // Handle incoming things
     this.$flowService.on('sessionJoin', (v) => {
     this.$flowService.on('sessionJoin', (v) => {
       if (v.id !== this.$route.params.sessId) {
       if (v.id !== this.$route.params.sessId) {
-        this.$router.push('/' + v.id) // Swap to ID
+        this.$router.push('/' + this.$route.params.context + '/' + v.id) // Swap to ID
       }
       }
     })
     })
     this.$flowService.on('registry', (v) => {
     this.$flowService.on('registry', (v) => {
@@ -317,7 +317,8 @@ export default {
   flex-direction: column;
   flex-direction: column;
 }
 }
 
 
-.flow-main .app-flow-container {
+.app-flow-container {
+  width:100%;
   position:relative;
   position:relative;
   flex:1;
   flex:1;
 }
 }

+ 5 - 2
browser/vue-flow/src/components/panel-inspector.vue

@@ -101,6 +101,7 @@ export default {
 .flow-inspector {
 .flow-inspector {
   font-size:12px;
   font-size:12px;
   flex:1;
   flex:1;
+  width:100%;
   overflow:hidden;
   overflow:hidden;
   height: available;
   height: available;
   display:flex;
   display:flex;
@@ -112,10 +113,10 @@ export default {
   padding:10px;
   padding:10px;
   display:flex;
   display:flex;
   flex-flow:column;
   flex-flow:column;
-  white-space: nowrap;
   width:100%;
   width:100%;
   flex-basis:100%;
   flex-basis:100%;
   transition: all var(--transition-speed);
   transition: all var(--transition-speed);
+  overflow-x:hidden;
   overflow-y:auto;
   overflow-y:auto;
 }
 }
 
 
@@ -124,7 +125,7 @@ export default {
   color: var(--normal);
   color: var(--normal);
 }
 }
 
 
-.flow-inspector__area {
+.flow-inspector__area{
   flex:1;
   flex:1;
 }
 }
 
 
@@ -150,6 +151,8 @@ export default {
 
 
 .flow-inspector__area .property {
 .flow-inspector__area .property {
   white-space: normal;
   white-space: normal;
+  word-wrap: break-word;
+
 }
 }
 
 
 .flow-inspector--properties-error {
 .flow-inspector--properties-error {

+ 4 - 3
browser/vue-flow/src/components/shared/hx-contextmenu.vue

@@ -21,8 +21,8 @@ module.exports = {
   },
   },
   computed: {
   computed: {
     style () {
     style () {
-      let x = this.x - 20
-      let y = this.y - 10
+      let x = this.x
+      let y = this.y
 
 
       if (!this.$el || !this.$parent || !this.$parent.$el) { return }
       if (!this.$el || !this.$parent || !this.$parent.$el) { return }
       const parentRect = this.$parent.$el.getBoundingClientRect()
       const parentRect = this.$parent.$el.getBoundingClientRect()
@@ -69,7 +69,8 @@ module.exports = {
   background: var(--background-secondary);
   background: var(--background-secondary);
   color: var(--normal);
   color: var(--normal);
   transition: opacity var(--transition-speed);
   transition: opacity var(--transition-speed);
-  border: solid 1px rgba(150,150,150,0.1)
+  border: solid 1px rgba(150,150,150,0.1);
+  box-shadow: 0 1px 4px rgba(0,0,0,0.5);
 }
 }
 
 
 .hx-context-menu.visible{
 .hx-context-menu.visible{

+ 2 - 0
browser/vue-flow/src/components/shared/hx-split.vue

@@ -79,6 +79,7 @@ export default {
 .split {
 .split {
   display: flex;
   display: flex;
   flex: 1;
   flex: 1;
+  max-width:100%;
   height: 100%;
   height: 100%;
 }
 }
 
 
@@ -109,6 +110,7 @@ export default {
 .split.resizeable.vertical > .splitter {
 .split.resizeable.vertical > .splitter {
   cursor: row-resize;
   cursor: row-resize;
 }
 }
+
 .split.resizeable.horizontal > .splitter {
 .split.resizeable.horizontal > .splitter {
   cursor: col-resize;
   cursor: col-resize;
 }
 }

+ 0 - 8
browser/vue-flow/src/main.js

@@ -1,16 +1,8 @@
 import Vue from 'vue'
 import Vue from 'vue'
 import App from './App.vue'
 import App from './App.vue'
-import FlowService from './services/flowservice'
 
 
 import router from './router'
 import router from './router'
 
 
-let targetws = 'ws://' + window.location.host + '/conn'
-if (window.location.protocol === 'https:') {
-  targetws = 'wss://' + window.location.host + '/conn'
-}
-
-Vue.use(FlowService, {location: targetws})
-
 window.app = new Vue({
 window.app = new Vue({
   el: '#app',
   el: '#app',
   router,
   router,

+ 2 - 1
browser/vue-flow/src/router/index.js

@@ -8,7 +8,8 @@ export default new Router({
   mode: 'history',
   mode: 'history',
   routes: [
   routes: [
     { path: '/', component: FlowMain },
     { path: '/', component: FlowMain },
-    { path: '/:sessId', component: FlowMain }
+    { path: '/:context', component: FlowMain },
+    { path: '/:context/:sessId', component: FlowMain }
   ]
   ]
 
 
 })
 })

+ 1 - 1
browser/vue-flow/webpack.config.js

@@ -122,7 +122,7 @@ if (process.env.NODE_ENV === 'development') {
     output: {
     output: {
       path: path.resolve(__dirname, './docs'),
       path: path.resolve(__dirname, './docs'),
       // publicPath: '/vue-edi-table/',
       // publicPath: '/vue-edi-table/',
-      publicPath: '/',
+      publicPath: '',
       filename: 'index.js'
       filename: 'index.js'
     }
     }
   })
   })

+ 140 - 0
go/src/flow/cmd/demo1/devops/devops.go

@@ -0,0 +1,140 @@
+package devops
+
+import (
+	"bufio"
+	"flow/registry"
+	"io"
+	"math/rand"
+	"strings"
+	"time"
+)
+
+// New create a new devops Registry
+func New() *registry.R {
+
+	r := registry.New()
+
+	r.Add(
+		dockerNew,
+		setWriter,
+		dockerTest,
+	).Tags("build")
+
+	return r
+}
+
+//////////////////////
+// DevOps SIM
+////////
+
+// DockerHandler example
+type DockerHandler struct {
+	out io.Writer
+}
+
+func dockerNew() DockerHandler {
+	return DockerHandler{}
+}
+
+func setWriter(out io.Writer, o DockerHandler) DockerHandler {
+	o.out = out
+	return o
+}
+
+//
+func dockerTest(handler DockerHandler, imageName string) DockerHandler {
+	sampleData := `
+	make: Entering directory '/home/stdio/coding/Projects/Flow'
+make -C go test
+make[1]: Entering directory '/home/stdio/coding/Projects/Flow/go'
+gocov test -race ./src/... | gocov report
+ok  	flow	1.020s	coverage: 84.1% of statements
+?   	flow/cmd/buildops	[no test files]
+?   	flow/cmd/demo1	[no test files]
+?   	flow/flowserver	[no test files]
+?   	flow/flowserver/flowmsg	[no test files]
+?   	flow/internal/assert	[no test files]
+ok  	flow/registry	1.011s	coverage: 100.0% of statements
+
+flow/flow.go		 Flow.String			 100.00% (11/11)
+flow/utils.go		 RandString			 100.00% (10/10)
+flow/flow.go		 @290:21			 100.00% (8/8)
+flow/flow.go		 Flow.MarshalJSON		 100.00% (8/8)
+flow/flow.go		 Flow.Const			 100.00% (7/7)
+flow/flow.go		 Flow.Var			 100.00% (6/6)
+flow/flow.go		 @315:21			 100.00% (6/6)
+flow/hook.go		 Hooks.Attach			 100.00% (3/3)
+flow/flow.go		 Flow.Run			 100.00% (2/2)
+flow/flow.go		 Flow.SetRegistry		 100.00% (2/2)
+flow/operation.go	 @107:12			 100.00% (2/2)
+flow/operation.go	 operation.ID			 100.00% (1/1)
+flow/operation.go	 opFunc				 100.00% (1/1)
+flow/operation.go	 opVar				 100.00% (1/1)
+flow/operation.go	 opConst			 100.00% (1/1)
+flow/flow.go		 Flow.In			 100.00% (1/1)
+flow/operation.go	 @220:12			 100.00% (1/1)
+flow/flow.go		 Flow.Res			 100.00% (1/1)
+flow/operation.go	 @221:12			 100.00% (1/1)
+flow/operation.go	 opIn				 100.00% (1/1)
+flow/flow.go		 Flow.Hook			 100.00% (1/1)
+flow/operation.go	 operation.Set			 100.00% (1/1)
+flow/hook.go		 Hooks.wait			 100.00% (1/1)
+flow/flow.go		 Flow.SetIDGen			 100.00% (1/1)
+flow/hook.go		 Hooks.finish			 100.00% (1/1)
+flow/operation.go	 operation.processWithCtx	 100.00% (1/1)
+flow/flow.go		 @49:13				 100.00% (1/1)
+flow/operation.go	 newOpCtx			 100.00% (1/1)
+flow/operation.go	 operation.Process		 100.00% (1/1)
+flow/hook.go		 Hooks.start			 100.00% (1/1)
+flow/flow.go		 New				 100.00% (1/1)
+flow/flow.go		 Flow.DefOp			 92.31% (12/13)
+flow/flow.go		 Flow.Op			 88.89% (16/18)
+flow/flow.go		 @240:21			 84.21% (16/19)
+flow/flow.go		 Flow.run			 84.21% (16/19)
+flow/operation.go	 @166:8				 80.00% (4/5)
+flow/hook.go		 Hooks.Trigger			 78.57% (11/14)
+flow/flow.go		 Flow.Analyse			 75.00% (3/4)
+flow/flow.go		 Flow.getOp			 75.00% (3/4)
+flow/flow.go		 Flow.Must			 66.67% (2/3)
+flow/operation.go	 @93:12				 66.67% (2/3)
+flow/operation.go	 @121:12			 65.22% (30/46)
+flow/operation.go	 @123:10			 33.33% (1/3)
+flow/hook.go		 Hooks.error			 0.00% (0/1)
+flow/operation.go	 opNil				 0.00% (0/1)
+flow/operation.go	 @229:12			 0.00% (0/1)
+flow/operation.go	 dumbSet			 0.00% (0/0)
+flow			 ------------------------	 84.10% (201/239)
+
+flow/registry/entry.go		 NewEntry		 100.00% (18/18)
+flow/registry/registry.go	 R.Get			 100.00% (12/12)
+flow/registry/registry.go	 R.Add			 100.00% (8/8)
+flow/registry/entry.go		 Entry.DescInputs	 100.00% (8/8)
+flow/registry/batch.go		 Batch			 100.00% (6/6)
+flow/registry/registry.go	 R.Register		 100.00% (5/5)
+flow/registry/entry.go		 Entry.Extra		 100.00% (4/4)
+flow/registry/registry.go	 R.Entry		 100.00% (4/4)
+flow/registry/registry.go	 R.Clone		 100.00% (4/4)
+flow/registry/entry.go		 Entry.Tags		 100.00% (4/4)
+flow/registry/entry.go		 Entry.DescOutput	 100.00% (4/4)
+flow/registry/registry.go	 R.Descriptions		 100.00% (4/4)
+flow/registry/batch.go		 EntryBatch.Extra	 100.00% (3/3)
+flow/registry/batch.go		 EntryBatch.DescOutput	 100.00% (3/3)
+flow/registry/batch.go		 EntryBatch.DescInputs	 100.00% (3/3)
+flow/registry/batch.go		 EntryBatch.Tags	 100.00% (3/3)
+flow/registry/entry.go		 Entry.Err		 100.00% (1/1)
+flow/registry/registry.go	 New			 100.00% (1/1)
+flow/registry			 ---------------------	 100.00% (95/95)
+
+Total Coverage: 88.62% (296/334)
+make[1]: Leaving directory '/home/stdio/coding/Projects/Flow/go'
+make: Leaving directory '/home/stdio/coding/Projects/Flow'`
+
+	scanner := bufio.NewScanner(strings.NewReader(sampleData))
+
+	for scanner.Scan() {
+		time.Sleep(time.Duration(rand.Intn(3000)) * time.Millisecond)
+		handler.out.Write([]byte(scanner.Text()))
+	}
+
+	return handler
+}

+ 20 - 114
go/src/flow/cmd/demo1/main.go

@@ -1,20 +1,22 @@
 package main
 package main
 
 
 import (
 import (
-	"bufio"
 	"errors"
 	"errors"
 	"flow"
 	"flow"
+	"flow/cmd/demo1/devops"
 	"flow/flowserver"
 	"flow/flowserver"
 	"flow/registry"
 	"flow/registry"
 	"fmt"
 	"fmt"
-	"io"
 	"log"
 	"log"
 	"math"
 	"math"
 	"math/rand"
 	"math/rand"
+	"net/http"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
 	"github.com/gohxs/prettylog"
 	"github.com/gohxs/prettylog"
+	"github.com/gohxs/webu"
+	"github.com/gohxs/webu/chain"
 )
 )
 
 
 func main() {
 func main() {
@@ -22,9 +24,9 @@ func main() {
 	log.Println("Running version:", flowserver.Version)
 	log.Println("Running version:", flowserver.Version)
 
 
 	// String functions
 	// String functions
-	registry.Batch(
-		registry.Add(strings.Split).DescInputs("string", "separator"),
-		registry.Add(strings.Join).DescInputs("", "sep"),
+	registry.Describer(
+		registry.Add(strings.Split).Inputs("string", "separator"),
+		registry.Add(strings.Join).Inputs("", "sep"),
 		registry.Add(strings.Compare, strings.Contains),
 		registry.Add(strings.Compare, strings.Contains),
 		registry.Register("Cat", func(a, b string) string { return a + " " + b }),
 		registry.Register("Cat", func(a, b string) string { return a + " " + b }),
 		registry.Register("ToString", func(a interface{}) string { return fmt.Sprint(a) }),
 		registry.Register("ToString", func(a interface{}) string { return fmt.Sprint(a) }),
@@ -35,8 +37,7 @@ func main() {
 		math.Abs, math.Cos, math.Sin, math.Exp, math.Exp2, math.Tanh, math.Max, math.Min,
 		math.Abs, math.Cos, math.Sin, math.Exp, math.Exp2, math.Tanh, math.Max, math.Min,
 	).Tags("math").Extra("style", registry.M{"color": "#386"})
 	).Tags("math").Extra("style", registry.M{"color": "#386"})
 
 
-	registry.Add(registry.Add(DockerPull)).Tags("devops") // Rand functions
-	registry.Batch(
+	registry.Describer(
 		registry.Add(rand.Int, rand.Intn, rand.Float64),
 		registry.Add(rand.Int, rand.Intn, rand.Float64),
 		registry.Register("Perm", func(n int) []int {
 		registry.Register("Perm", func(n int) []int {
 			if n > 10 { // Limiter for safety
 			if n > 10 { // Limiter for safety
@@ -50,7 +51,7 @@ func main() {
 	registry.Add(testErrorPanic, testErrorDelayed, testRandomError).
 	registry.Add(testErrorPanic, testErrorDelayed, testRandomError).
 		Tags("testing-errors")
 		Tags("testing-errors")
 
 
-	registry.Batch(
+	registry.Describer(
 		registry.Register("wait", wait),
 		registry.Register("wait", wait),
 		registry.Register("waitRandom", waitRandom),
 		registry.Register("waitRandom", waitRandom),
 	).Tags("testing-time").Extra("style", map[string]string{"color": "#8a5"})
 	).Tags("testing-time").Extra("style", map[string]string{"color": "#8a5"})
@@ -58,8 +59,17 @@ func main() {
 	addr := ":2015"
 	addr := ":2015"
 	log.Println("Starting server  at:", addr)
 	log.Println("Starting server  at:", addr)
 
 
-	f := flowserver.FlowServer{}
-	f.ListenAndServe(addr)
+	c := chain.New(webu.ChainLogger(prettylog.New("req")))
+
+	mux := http.NewServeMux()
+
+	mux.Handle("/", c.Build(http.RedirectHandler("/default/", 302).ServeHTTP))
+	mux.Handle("/default/", c.Build(flowserver.New(registry.Global, "default").ServeHTTP))
+
+	mux.Handle("/devops/", c.Build(flowserver.New(devops.New(), "devops").ServeHTTP))
+
+	// Context registry
+	http.ListenAndServe(addr, mux)
 }
 }
 
 
 func wait(data flow.Data, n int) flow.Data {
 func wait(data flow.Data, n int) flow.Data {
@@ -96,107 +106,3 @@ func testRandomError(d flow.Data) (flow.Data, error) {
 	}
 	}
 	return d, nil
 	return d, nil
 }
 }
-
-//////////////////////
-// DevOps SIM
-////////
-//
-type DockerImage struct{}
-
-//
-func DockerPull(w io.Writer, imageName string) DockerImage {
-	sampleData := `
-	make: Entering directory '/home/stdio/coding/Projects/Flow'
-make -C go test
-make[1]: Entering directory '/home/stdio/coding/Projects/Flow/go'
-gocov test -race ./src/... | gocov report
-ok  	flow	1.020s	coverage: 84.1% of statements
-?   	flow/cmd/buildops	[no test files]
-?   	flow/cmd/demo1	[no test files]
-?   	flow/flowserver	[no test files]
-?   	flow/flowserver/flowmsg	[no test files]
-?   	flow/internal/assert	[no test files]
-ok  	flow/registry	1.011s	coverage: 100.0% of statements
-
-flow/flow.go		 Flow.String			 100.00% (11/11)
-flow/utils.go		 RandString			 100.00% (10/10)
-flow/flow.go		 @290:21			 100.00% (8/8)
-flow/flow.go		 Flow.MarshalJSON		 100.00% (8/8)
-flow/flow.go		 Flow.Const			 100.00% (7/7)
-flow/flow.go		 Flow.Var			 100.00% (6/6)
-flow/flow.go		 @315:21			 100.00% (6/6)
-flow/hook.go		 Hooks.Attach			 100.00% (3/3)
-flow/flow.go		 Flow.Run			 100.00% (2/2)
-flow/flow.go		 Flow.SetRegistry		 100.00% (2/2)
-flow/operation.go	 @107:12			 100.00% (2/2)
-flow/operation.go	 operation.ID			 100.00% (1/1)
-flow/operation.go	 opFunc				 100.00% (1/1)
-flow/operation.go	 opVar				 100.00% (1/1)
-flow/operation.go	 opConst			 100.00% (1/1)
-flow/flow.go		 Flow.In			 100.00% (1/1)
-flow/operation.go	 @220:12			 100.00% (1/1)
-flow/flow.go		 Flow.Res			 100.00% (1/1)
-flow/operation.go	 @221:12			 100.00% (1/1)
-flow/operation.go	 opIn				 100.00% (1/1)
-flow/flow.go		 Flow.Hook			 100.00% (1/1)
-flow/operation.go	 operation.Set			 100.00% (1/1)
-flow/hook.go		 Hooks.wait			 100.00% (1/1)
-flow/flow.go		 Flow.SetIDGen			 100.00% (1/1)
-flow/hook.go		 Hooks.finish			 100.00% (1/1)
-flow/operation.go	 operation.processWithCtx	 100.00% (1/1)
-flow/flow.go		 @49:13				 100.00% (1/1)
-flow/operation.go	 newOpCtx			 100.00% (1/1)
-flow/operation.go	 operation.Process		 100.00% (1/1)
-flow/hook.go		 Hooks.start			 100.00% (1/1)
-flow/flow.go		 New				 100.00% (1/1)
-flow/flow.go		 Flow.DefOp			 92.31% (12/13)
-flow/flow.go		 Flow.Op			 88.89% (16/18)
-flow/flow.go		 @240:21			 84.21% (16/19)
-flow/flow.go		 Flow.run			 84.21% (16/19)
-flow/operation.go	 @166:8				 80.00% (4/5)
-flow/hook.go		 Hooks.Trigger			 78.57% (11/14)
-flow/flow.go		 Flow.Analyse			 75.00% (3/4)
-flow/flow.go		 Flow.getOp			 75.00% (3/4)
-flow/flow.go		 Flow.Must			 66.67% (2/3)
-flow/operation.go	 @93:12				 66.67% (2/3)
-flow/operation.go	 @121:12			 65.22% (30/46)
-flow/operation.go	 @123:10			 33.33% (1/3)
-flow/hook.go		 Hooks.error			 0.00% (0/1)
-flow/operation.go	 opNil				 0.00% (0/1)
-flow/operation.go	 @229:12			 0.00% (0/1)
-flow/operation.go	 dumbSet			 0.00% (0/0)
-flow			 ------------------------	 84.10% (201/239)
-
-flow/registry/entry.go		 NewEntry		 100.00% (18/18)
-flow/registry/registry.go	 R.Get			 100.00% (12/12)
-flow/registry/registry.go	 R.Add			 100.00% (8/8)
-flow/registry/entry.go		 Entry.DescInputs	 100.00% (8/8)
-flow/registry/batch.go		 Batch			 100.00% (6/6)
-flow/registry/registry.go	 R.Register		 100.00% (5/5)
-flow/registry/entry.go		 Entry.Extra		 100.00% (4/4)
-flow/registry/registry.go	 R.Entry		 100.00% (4/4)
-flow/registry/registry.go	 R.Clone		 100.00% (4/4)
-flow/registry/entry.go		 Entry.Tags		 100.00% (4/4)
-flow/registry/entry.go		 Entry.DescOutput	 100.00% (4/4)
-flow/registry/registry.go	 R.Descriptions		 100.00% (4/4)
-flow/registry/batch.go		 EntryBatch.Extra	 100.00% (3/3)
-flow/registry/batch.go		 EntryBatch.DescOutput	 100.00% (3/3)
-flow/registry/batch.go		 EntryBatch.DescInputs	 100.00% (3/3)
-flow/registry/batch.go		 EntryBatch.Tags	 100.00% (3/3)
-flow/registry/entry.go		 Entry.Err		 100.00% (1/1)
-flow/registry/registry.go	 New			 100.00% (1/1)
-flow/registry			 ---------------------	 100.00% (95/95)
-
-Total Coverage: 88.62% (296/334)
-make[1]: Leaving directory '/home/stdio/coding/Projects/Flow/go'
-make: Leaving directory '/home/stdio/coding/Projects/Flow'`
-
-	scanner := bufio.NewScanner(strings.NewReader(sampleData))
-
-	for scanner.Scan() {
-		time.Sleep(time.Duration(rand.Intn(4)) * time.Second)
-		w.Write([]byte(scanner.Text()))
-	}
-
-	return DockerImage{}
-}

+ 12 - 7
go/src/flow/flowserver/flowbuilder.go

@@ -6,6 +6,7 @@ import (
 	"flow/registry"
 	"flow/registry"
 	"log"
 	"log"
 	"reflect"
 	"reflect"
+	"strings"
 )
 )
 
 
 // Node that will contain registry src
 // Node that will contain registry src
@@ -29,7 +30,7 @@ type FlowDocument struct {
 	Links []Link `json:"links"`
 	Links []Link `json:"links"`
 }
 }
 
 
-// FlowBuild build a flowGraph
+// FlowBuild build a flowGraph from incoming web ui json
 func FlowBuild(rawData []byte, r *registry.R) (*flow.Flow, error) {
 func FlowBuild(rawData []byte, r *registry.R) (*flow.Flow, error) {
 	doc := FlowDocument{[]Node{}, []Link{}}
 	doc := FlowDocument{[]Node{}, []Link{}}
 	err := json.Unmarshal(rawData, &doc)
 	err := json.Unmarshal(rawData, &doc)
@@ -78,17 +79,21 @@ func FlowBuild(rawData []byte, r *registry.R) (*flow.Flow, error) {
 				param[l.In] = f.Var(from.ID, from.Prop["init"])
 				param[l.In] = f.Var(from.ID, from.Prop["init"])
 			case "Const":
 			case "Const":
 				// XXX: Automate this in a func
 				// XXX: Automate this in a func
-				newVal := reflect.New(entry.Inputs[l.In])
 				raw := from.Label
 				raw := from.Label
 				//raw := from.Prop["value"]
 				//raw := from.Prop["value"]
-				if _, ok := newVal.Interface().(*string); ok {
-					log.Println("Trying to unmarshal a string")
-					raw = "\"" + raw + "\""
+				raw = strings.TrimSpace(raw)
+				if raw[0] == '"' { // its a string
+					log.Println("Unmashal string")
+					var val string
+					json.Unmarshal([]byte(raw), &val)
+					param[l.In] = val
+					continue
 				}
 				}
-				log.Println("Will unmarshal raw:", raw)
+				log.Println("Will unmarshal to input:", raw)
+				newVal := reflect.New(entry.Inputs[l.In])
 				err := json.Unmarshal([]byte(raw), newVal.Interface())
 				err := json.Unmarshal([]byte(raw), newVal.Interface())
 				if err != nil {
 				if err != nil {
-					// ignore error
+					// ignore error?
 					log.Println("unmarshalling Error", err)
 					log.Println("unmarshalling Error", err)
 					//param[l.In] = nil
 					//param[l.In] = nil
 					//continue
 					//continue

+ 30 - 33
go/src/flow/flowserver/flowserver.go

@@ -1,15 +1,15 @@
 package flowserver
 package flowserver
 
 
 import (
 import (
+	"flow/registry"
 	"log"
 	"log"
 	"net/http"
 	"net/http"
 	"net/http/httputil"
 	"net/http/httputil"
 	"net/url"
 	"net/url"
 	"os"
 	"os"
+	"strings"
 
 
-	"github.com/gohxs/prettylog"
 	"github.com/gohxs/webu"
 	"github.com/gohxs/webu"
-	"github.com/gohxs/webu/chain"
 )
 )
 
 
 //go:generate go get github.com/gohxs/genversion
 //go:generate go get github.com/gohxs/genversion
@@ -17,47 +17,44 @@ import (
 //
 //
 
 
 // FlowServer structure
 // FlowServer structure
-type FlowServer struct{}
-
-// ListenAndServe starts the httpserver
-// It will listen on default port 2015 and increase if port is in use
-func (f *FlowServer) ListenAndServe(addr string) error {
-	fsm := NewFlowSessionManager()
+type FlowServer struct {
+	mux *http.ServeMux
+}
 
 
-	c := chain.New(webu.ChainLogger(prettylog.New("req")))
+// New creates a New flow server
+func New(r *registry.R, store string) *FlowServer {
+	if r == nil {
+		r = registry.Global
+	}
 
 
 	mux := http.NewServeMux()
 	mux := http.NewServeMux()
-	mux.Handle("/conn", c.Build(fsm.ServeHTTP))
+	mux.Handle("/conn", NewFlowSessionManager(r, store))
 
 
 	if os.Getenv("DEBUG") == "1" {
 	if os.Getenv("DEBUG") == "1" {
 		log.Println("DEBUG MODE: reverse proxy localhost:8081")
 		log.Println("DEBUG MODE: reverse proxy localhost:8081")
 		proxyURL, err := url.Parse("http://localhost:8081")
 		proxyURL, err := url.Parse("http://localhost:8081")
 		if err != nil {
 		if err != nil {
-			return err
+			return nil
 		}
 		}
-		mux.Handle("/", c.Build(httputil.NewSingleHostReverseProxy(proxyURL).ServeHTTP))
+		mux.Handle("/", httputil.NewSingleHostReverseProxy(proxyURL))
 	} else {
 	} else {
-		mux.Handle("/", c.Build(webu.StaticHandler("web", "index.html")))
+		mux.Handle("/", webu.StaticHandler("web", "index.html"))
 	}
 	}
 
 
-	return http.ListenAndServe(addr, mux)
-
-	////////////////////
-	// Server starter
-	/////
-	//port := 2015
-	//for {
-	//addr := fmt.Sprintf(":%d", port)
-	//s, err := net.Listen("tcp", addr)
-	//if err != nil {
-	//log.Println("Listen error:", err)
-	//port++
-	//continue
-	//}
-	//log.Println("Listening at:", addr)
-	//err = http.Serve(s, mux)
-	//if err != nil {
-	//log.Fatal(err)
-	//}
-	//}
+	return &FlowServer{mux}
+}
+
+func (f *FlowServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	// Manual routing here
+	// Grab last part of path
+	urlParts := strings.Split(r.URL.Path, "/")
+	prefixToRemove := strings.Join(urlParts[:len(urlParts)-1], "/")
+	http.StripPrefix(prefixToRemove, f.mux).ServeHTTP(w, r)
+
+}
+
+// ListenAndServe starts the httpserver
+// It will listen on default port 2015 and increase if port is in use
+func (f *FlowServer) ListenAndServe(addr string) error {
+	return http.ListenAndServe(addr, f.mux)
 }
 }

+ 7 - 26
go/src/flow/flowserver/session.go

@@ -5,23 +5,17 @@ import (
 	"errors"
 	"errors"
 	"flow"
 	"flow"
 	"flow/flowserver/flowmsg"
 	"flow/flowserver/flowmsg"
-	"flow/registry"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
 	"os"
 	"os"
-	"path/filepath"
 	"sync"
 	"sync"
 	"time"
 	"time"
 
 
 	"github.com/gorilla/websocket"
 	"github.com/gorilla/websocket"
 )
 )
 
 
-const (
-	storePath = "store"
-)
-
 // NodeActivity when nodes are processing
 // NodeActivity when nodes are processing
 type NodeActivity struct {
 type NodeActivity struct {
 	Status    string    `json:"status"` // nodeStatus, Running, error, result
 	Status    string    `json:"status"` // nodeStatus, Running, error, result
@@ -34,7 +28,7 @@ type NodeActivity struct {
 // FlowSession Create a session and link clients
 // FlowSession Create a session and link clients
 type FlowSession struct {
 type FlowSession struct {
 	sync.Mutex
 	sync.Mutex
-	mgr *FlowSessionManager
+	manager *FlowSessionManager
 
 
 	ID string // Random handle for sessionID
 	ID string // Random handle for sessionID
 	// List of clients on this session
 	// List of clients on this session
@@ -48,11 +42,11 @@ type FlowSession struct {
 }
 }
 
 
 //NewSession creates and initializes a NewSession
 //NewSession creates and initializes a NewSession
-func NewSession(mgr *FlowSessionManager, ID string) *FlowSession {
+func NewSession(fsm *FlowSessionManager, ID string) *FlowSession {
 	// Or load
 	// Or load
 	//
 	//
 	//
 	//
-	fpath, err := pathFor(ID)
+	fpath, err := fsm.pathFor(ID)
 	if err != nil {
 	if err != nil {
 		log.Println("Error fetching filepath", err)
 		log.Println("Error fetching filepath", err)
 	}
 	}
@@ -67,7 +61,7 @@ func NewSession(mgr *FlowSessionManager, ID string) *FlowSession {
 
 
 	s := &FlowSession{
 	s := &FlowSession{
 		Mutex:        sync.Mutex{},
 		Mutex:        sync.Mutex{},
-		mgr:          mgr,
+		manager:      fsm,
 		ID:           ID,
 		ID:           ID,
 		clients:      []*websocket.Conn{},
 		clients:      []*websocket.Conn{},
 		Chat:         ChatRoom{},
 		Chat:         ChatRoom{},
@@ -87,7 +81,7 @@ func (s *FlowSession) ClientAdd(c *websocket.Conn) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	desc, err := registry.Descriptions()
+	desc, err := s.manager.registry.Descriptions()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -149,7 +143,7 @@ func (s *FlowSession) DocumentSave() error {
 	s.Lock()
 	s.Lock()
 	defer s.Unlock()
 	defer s.Unlock()
 
 
-	fpath, err := pathFor(s.ID)
+	fpath, err := s.manager.pathFor(s.ID)
 	if err != nil {
 	if err != nil {
 		log.Println("path error", err)
 		log.Println("path error", err)
 		return err
 		return err
@@ -188,7 +182,7 @@ func (s *FlowSession) NodeRun(c *websocket.Conn, data []byte) error {
 	go func() {
 	go func() {
 		log.Printf("Building flow from '%s'\n", string(s.RawDoc))
 		log.Printf("Building flow from '%s'\n", string(s.RawDoc))
 
 
-		localr := registry.Global.Clone()
+		localr := s.manager.registry.Clone()
 		//Add our log func that is not in global registry
 		//Add our log func that is not in global registry
 		localr.Register("Notify", func(v flow.Data, msg string) flow.Data {
 		localr.Register("Notify", func(v flow.Data, msg string) flow.Data {
 			s.Notify(msg)
 			s.Notify(msg)
@@ -287,16 +281,3 @@ func (s *FlowSession) broadcast(c *websocket.Conn, v interface{}) error {
 	return nil
 	return nil
 
 
 }
 }
-
-func pathFor(ID string) (string, error) {
-	{
-		err := os.MkdirAll(storePath, 0755)
-		if err != nil {
-			return "", err
-		}
-	}
-	fpath := filepath.Clean(ID)
-	_, fpath = filepath.Split(fpath)
-	fpath = filepath.Join(storePath, fpath)
-	return fpath, nil
-}

+ 30 - 1
go/src/flow/flowserver/sessionmgr.go

@@ -5,8 +5,10 @@ import (
 	"errors"
 	"errors"
 	"flow"
 	"flow"
 	"flow/flowserver/flowmsg"
 	"flow/flowserver/flowmsg"
+	"flow/registry"
 	"log"
 	"log"
 	"net/http"
 	"net/http"
+	"os"
 	"path/filepath"
 	"path/filepath"
 	"runtime"
 	"runtime"
 	"sync"
 	"sync"
@@ -16,6 +18,9 @@ import (
 
 
 //FlowSessionManager or FlowServerCore
 //FlowSessionManager or FlowServerCore
 type FlowSessionManager struct {
 type FlowSessionManager struct {
+	name     string
+	registry *registry.R
+	store    string
 	// List of flow sessions?
 	// List of flow sessions?
 	sessions map[string]*FlowSession
 	sessions map[string]*FlowSession
 	chats    map[string]*ChatRoom
 	chats    map[string]*ChatRoom
@@ -24,8 +29,10 @@ type FlowSessionManager struct {
 }
 }
 
 
 //NewFlowSessionManager creates a New initialized FlowSessionManager
 //NewFlowSessionManager creates a New initialized FlowSessionManager
-func NewFlowSessionManager() *FlowSessionManager {
+func NewFlowSessionManager(r *registry.R, store string) *FlowSessionManager {
 	return &FlowSessionManager{
 	return &FlowSessionManager{
+		registry: r,
+		store:    store,
 		sessions: map[string]*FlowSession{},
 		sessions: map[string]*FlowSession{},
 	}
 	}
 }
 }
@@ -65,6 +72,7 @@ var upgrader = websocket.Upgrader{}
 
 
 func (fsm *FlowSessionManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 func (fsm *FlowSessionManager) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 
 
+	// Contextual flowsession
 	c, err := upgrader.Upgrade(w, r, nil)
 	c, err := upgrader.Upgrade(w, r, nil)
 	if err != nil {
 	if err != nil {
 		log.Println("upgrade:", err)
 		log.Println("upgrade:", err)
@@ -188,6 +196,27 @@ func (fsm *FlowSessionManager) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	log.Println("ws Is disconnecting", r.RemoteAddr)
 	log.Println("ws Is disconnecting", r.RemoteAddr)
 }
 }
 
 
+const (
+	storePath = "store"
+)
+
+func (fsm *FlowSessionManager) pathFor(ID string) (string, error) {
+	{
+		err := os.MkdirAll(storePath, 0755)
+		if err != nil {
+			return "", err
+		}
+		err = os.MkdirAll(filepath.Join(storePath, fsm.store), 0755)
+		if err != nil {
+			return "", err
+		}
+	}
+	fpath := filepath.Clean(ID)
+	_, fpath = filepath.Split(fpath)
+	fpath = filepath.Join(storePath, fsm.store, fpath)
+	return fpath, nil
+}
+
 func e(err error) bool {
 func e(err error) bool {
 	if err == nil {
 	if err == nil {
 		return false
 		return false

+ 0 - 51
go/src/flow/registry/batch.go

@@ -1,51 +0,0 @@
-package registry
-
-//EntryBatch helper to batch set properties
-type EntryBatch []*Entry
-
-// Batch returns a batch of entries for easy manipulation
-func Batch(params ...interface{}) EntryBatch {
-	ret := EntryBatch{}
-	for _, el := range params {
-		switch v := el.(type) {
-		case EntryBatch:
-			ret = append(ret, v...)
-		case *Entry:
-			ret = append(ret, v)
-		}
-	}
-	return ret
-}
-
-//Tags set categories of the group
-func (eg EntryBatch) Tags(cat ...string) EntryBatch {
-	for _, e := range eg {
-		e.Tags(cat...)
-	}
-	return eg
-}
-
-// DescInputs describe inputs
-func (eg EntryBatch) DescInputs(input ...string) EntryBatch {
-	for _, e := range eg {
-		e.DescInputs(input...)
-	}
-	return eg
-
-}
-
-// DescOutput describe inputs
-func (eg EntryBatch) DescOutput(v string) EntryBatch {
-	for _, e := range eg {
-		e.DescOutput(v)
-	}
-	return eg
-}
-
-// Extra set extras of the group
-func (eg EntryBatch) Extra(name string, value interface{}) EntryBatch {
-	for _, e := range eg {
-		e.Extra(name, value)
-	}
-	return eg
-}

+ 95 - 0
go/src/flow/registry/describer.go

@@ -0,0 +1,95 @@
+package registry
+
+// Description of an entry
+type Description struct {
+	Name string   `json:"name"`
+	Desc string   `json:"description"`
+	Tags []string `json:"categories"`
+
+	//InputType
+	Inputs []DescType `json:"inputs"`
+	Output DescType   `json:"output"`
+
+	Extra map[string]interface{} `json:"extra"`
+}
+
+//EDescriber helper to batch set properties
+type EDescriber []*Entry
+
+// Describer returns a batch of entries for easy manipulation
+func Describer(params ...interface{}) EDescriber {
+	ret := EDescriber{}
+	for _, el := range params {
+		switch v := el.(type) {
+		case EDescriber:
+			ret = append(ret, v...)
+		case *Entry:
+			ret = append(ret, v)
+		}
+	}
+	return ret
+}
+
+// Description set node description
+func (d EDescriber) Description(m string) EDescriber {
+	for _, e := range d {
+		if e.Description == nil {
+			continue
+		}
+
+		e.Description.Desc = m
+	}
+	return d
+
+}
+
+//Tags set categories of the group
+func (d EDescriber) Tags(tags ...string) EDescriber {
+	for _, e := range d {
+		if e.Description == nil {
+			continue
+		}
+		e.Description.Tags = tags
+	}
+	return d
+}
+
+// Inputs describe inputs
+func (d EDescriber) Inputs(inputs ...string) EDescriber {
+	for _, e := range d {
+		if e.Description == nil {
+			continue
+		}
+
+		for i, dstr := range inputs {
+			if i >= len(e.Description.Inputs) { // do nothing
+				break // next entry
+			}
+			curDesc := e.Description.Inputs[i]
+			e.Description.Inputs[i] = DescType{curDesc.Type, dstr}
+		}
+	}
+	return d
+}
+
+// Output describe the output
+func (d EDescriber) Output(output string) EDescriber {
+	for _, e := range d {
+		if e.Description == nil {
+			continue
+		}
+		e.Description.Output = DescType{e.Description.Output.Type, output}
+	}
+	return d
+}
+
+// Extra set extras of the group
+func (d EDescriber) Extra(name string, value interface{}) EDescriber {
+	for _, e := range d {
+		if e.Description == nil {
+			continue
+		}
+		e.Description.Extra[name] = value
+	}
+	return d
+}

+ 3 - 3
go/src/flow/registry/batch_test.go

@@ -11,15 +11,15 @@ func TestMakeBatch(t *testing.T) {
 	a := assert.A(t)
 	a := assert.A(t)
 	r := registry.New()
 	r := registry.New()
 
 
-	b := registry.Batch(
+	b := registry.Describer(
 		r.Add(strings.Split, strings.Join),
 		r.Add(strings.Split, strings.Join),
 		r.Add(strings.Compare),
 		r.Add(strings.Compare),
 		r.Register("named", strings.Compare),
 		r.Register("named", strings.Compare),
 	)
 	)
 	a.Eq(len(b), 4, "should have 3 entries in batch")
 	a.Eq(len(b), 4, "should have 3 entries in batch")
 
 
-	b.DescInputs("str")
-	b.DescOutput("result")
+	b.Inputs("str")
+	b.Output("result")
 
 
 	for _, en := range b {
 	for _, en := range b {
 		a.Eq(en.Description.Inputs[0].Name, "str", "first input should be string")
 		a.Eq(en.Description.Inputs[0].Name, "str", "first input should be string")

+ 5 - 61
go/src/flow/registry/entry.go

@@ -11,18 +11,6 @@ type DescType struct {
 	Name string `json:"name"`
 	Name string `json:"name"`
 }
 }
 
 
-// Description of an entry
-type Description struct {
-	Name string   `json:"name"`
-	Tags []string `json:"categories"`
-
-	//InputType
-	Inputs []DescType `json:"inputs"`
-	Output DescType   `json:"output"`
-
-	Extra map[string]interface{} `json:"extra"`
-}
-
 // Entry contains a function description params etc
 // Entry contains a function description params etc
 type Entry struct {
 type Entry struct {
 	registry    *R
 	registry    *R
@@ -66,56 +54,12 @@ func NewEntry(r *R, fn interface{}) *Entry {
 	return e
 	return e
 }
 }
 
 
+// Describer return a description builder
+func (e *Entry) Describer() EDescriber {
+	return Describer(e)
+}
+
 // Err returns error of the entry if any
 // Err returns error of the entry if any
 func (e *Entry) Err() error {
 func (e *Entry) Err() error {
 	return e.err
 	return e.err
 }
 }
-
-/////////////
-// Descriptions
-/////
-
-//Tags of the entry
-func (e *Entry) Tags(cat ...string) *Entry {
-	if e.err != nil {
-		return e
-	}
-	e.Description.Tags = cat
-	return e
-}
-
-// DescInputs description for Inputs
-func (e *Entry) DescInputs(desc ...string) *Entry {
-	if e.err != nil {
-		return e
-	}
-	for i, d := range desc {
-		if i >= len(e.Description.Inputs) { // do nothing
-			return e
-		}
-		curDesc := e.Description.Inputs[i]
-		e.Description.Inputs[i] = DescType{curDesc.Type, d}
-	}
-	return e
-}
-
-// DescOutput description for Input
-func (e *Entry) DescOutput(desc string) *Entry {
-	if e.err != nil {
-		return e
-	}
-	e.Description.Output = DescType{
-		e.Description.Output.Type,
-		desc,
-	}
-	return e
-}
-
-// Extra information on entry
-func (e *Entry) Extra(name string, extra interface{}) *Entry {
-	if e.err != nil {
-		return e
-	}
-	e.Description.Extra[name] = extra
-	return e
-}

+ 11 - 10
go/src/flow/registry/entry_test.go

@@ -24,7 +24,7 @@ func TestDescription(t *testing.T) {
 	r := registry.New()
 	r := registry.New()
 
 
 	e := registry.NewEntry(r, func(a int) int { return 0 })
 	e := registry.NewEntry(r, func(a int) int { return 0 })
-	e.Tags("a", "b")
+	e.Describer().Tags("a", "b")
 	a.Eq(e.Err(), nil, "should not fail setting categories")
 	a.Eq(e.Err(), nil, "should not fail setting categories")
 	a.Eq(len(e.Description.Tags), 2, "should have 2 categories")
 	a.Eq(len(e.Description.Tags), 2, "should have 2 categories")
 	a.Eq(len(e.Description.Inputs), 1, "Should have 2 input description")
 	a.Eq(len(e.Description.Inputs), 1, "Should have 2 input description")
@@ -32,18 +32,18 @@ func TestDescription(t *testing.T) {
 	e2 := registry.NewEntry(r, func(a, b int) int { return 0 })
 	e2 := registry.NewEntry(r, func(a, b int) int { return 0 })
 	a.Eq(len(e2.Description.Inputs), 2, "Should have 2 input description")
 	a.Eq(len(e2.Description.Inputs), 2, "Should have 2 input description")
 
 
-	e.DescInputs("input")
+	e.Describer().Inputs("input")
 	a.Eq(e.Err(), nil, "should not fail setting input name")
 	a.Eq(e.Err(), nil, "should not fail setting input name")
 	a.Eq(e.Description.Inputs[0].Name, "input", "should have the input description")
 	a.Eq(e.Description.Inputs[0].Name, "input", "should have the input description")
 
 
-	e.DescInputs("input", "2", "3")
+	e.Describer().Inputs("input", "2", "3")
 	a.Eq(len(e.Description.Inputs), 1, "should have only one input")
 	a.Eq(len(e.Description.Inputs), 1, "should have only one input")
 
 
-	e.DescOutput("output name")
+	e.Describer().Output("output name")
 	a.Eq(e.Err(), nil, "should not fail setting input description")
 	a.Eq(e.Err(), nil, "should not fail setting input description")
 	a.Eq(e.Description.Output.Name, "output name", "output description should be the same")
 	a.Eq(e.Description.Output.Name, "output name", "output description should be the same")
 
 
-	e.Extra("test", 123)
+	e.Describer().Extra("test", 123)
 	a.Eq(e.Err(), nil, "should not fail setting extra doc")
 	a.Eq(e.Err(), nil, "should not fail setting extra doc")
 	a.Eq(e.Description.Extra["test"], 123, "extra text should be as expected")
 	a.Eq(e.Description.Extra["test"], 123, "extra text should be as expected")
 }
 }
@@ -52,14 +52,15 @@ func TestDescriptionError(t *testing.T) {
 	a := assert.A(t)
 	a := assert.A(t)
 	r := registry.New()
 	r := registry.New()
 	e := registry.NewEntry(r, "string")
 	e := registry.NewEntry(r, "string")
+
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
-	e.Tags("a", "b")
+	e.Describer().Tags("a", "b")
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
-	e.DescInputs("input")
+	e.Describer().Inputs("input")
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
-	e.DescOutput("output")
+	e.Describer().Output("output")
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
-	e.Extra("test", 123)
+	e.Describer().Extra("test", 123)
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
 	a.Eq(e.Err(), registry.ErrNotAFunc, "entry is not a function")
 
 
 }
 }
@@ -68,7 +69,7 @@ func TestEntryBatch(t *testing.T) {
 	a := assert.A(t)
 	a := assert.A(t)
 	r := registry.New()
 	r := registry.New()
 
 
-	b := registry.Batch(
+	b := registry.Describer(
 		registry.NewEntry(r, func() int { return 0 }),
 		registry.NewEntry(r, func() int { return 0 }),
 		registry.NewEntry(r, func() int { return 0 }),
 		registry.NewEntry(r, func() int { return 0 }),
 		registry.NewEntry(r, func() int { return 0 }),
 		registry.NewEntry(r, func() int { return 0 }),

+ 2 - 2
go/src/flow/registry/registry.go

@@ -38,9 +38,9 @@ func (r *R) Clone() *R {
 }
 }
 
 
 // Add unnamed function
 // Add unnamed function
-func (r *R) Add(fns ...interface{}) EntryBatch {
+func (r *R) Add(fns ...interface{}) EDescriber {
 
 
-	b := EntryBatch{}
+	b := EDescriber{}
 	for _, fn := range fns {
 	for _, fn := range fns {
 		if reflect.TypeOf(fn).Kind() != reflect.Func {
 		if reflect.TypeOf(fn).Kind() != reflect.Func {
 			return nil
 			return nil

+ 21 - 0
test.txt

@@ -0,0 +1,21 @@
+Using default tag: latest
+latest: Pulling from base/archlinux
+6bf09137c659: Pulling fs layer
+e412e19e1798: Pulling fs layer
+dc224f9fe847: Pulling fs layer
+e1aef29dab31: Pulling fs layer
+e1aef29dab31: Waiting
+e412e19e1798: Verifying Checksum
+e412e19e1798: Download complete
+6bf09137c659: Verifying Checksum
+6bf09137c659: Download complete
+dc224f9fe847: Verifying Checksum
+dc224f9fe847: Download complete
+e1aef29dab31: Verifying Checksum
+e1aef29dab31: Download complete
+6bf09137c659: Pull complete
+e412e19e1798: Pull complete
+dc224f9fe847: Pull complete
+e1aef29dab31: Pull complete
+Digest: sha256:ea17a7802b4effc6c5239d01829336ee66fb37d842c0cd7d6e7067616cd0c994
+Status: Downloaded newer image for base/archlinux:latest