浏览代码

Documentation, removed err from operations

* Operations will give error at runtime
luis 7 年之前
父节点
当前提交
00e48040b1

+ 6 - 6
browser/vue-flow/src/assets/dark-theme.css

@@ -5,11 +5,7 @@
   --background-transparent: rgba(48, 48, 48, 1);
   --background-secondary: #424242;
   --background-tertiary: #323232;
-
-  /*--background-secondary: #424242;*/
-
-  /*--background-tertiary: #323232;*/
-  --normal: #aaa;
+  --normal: #bfbfbf;
   --normal-secondary: #777;
 
   /* --primary: #f57c00 ; */
@@ -18,7 +14,7 @@
   --primary-lighter: #ff8c20;
   --node-label: var(--normal);
   --node-socket: var(--primary);
-  --node-socket--withvalue: black;
+  --node-socket--withvalue: #44f;
   --link-hover: var(--primary);
   --selector-background: rgba(200, 150, 50, 0.1);
   --selector-color: var(--primary);
@@ -54,3 +50,7 @@
 .dark .flow-node__activity {
   fill: var(--background-secondary);
 }
+
+.dark .flow-funcs__src {
+  color: #fff;
+}

+ 2 - 2
browser/vue-flow/src/assets/default-theme.css

@@ -15,11 +15,11 @@
   --secondary-inverse: #fff;
   --node-label: #fff;
   --node-socket: #444;
-  --node-socket--withvalue: #44a;
+  --node-socket--withvalue: #44F;
   --link-hover: #f00;
   --selector-background: rgba(0, 0, 200, 0.1);
   --selector-color: var(--primary);
-  --transition-speed: 0.2s;
+  --transition-speed: 0.3s;
   --transition-speed-slow: 0.7s;
 }
 

+ 5 - 2
browser/vue-flow/src/assets/doc/appinfo.md

@@ -1,16 +1,19 @@
 #### Editor
 
 * **Collaboration**: Using the same url address, others can join the session
-* **Pan**: Drag with Middle mouse or Ctrl+left mouse button
+* **Pan**: Drag with Middle mouse or `Ctrl`+left mouse button
 * **Zoom**: Mouse wheel up and down to zoom in and out
 * **Reset**: Reset view by pressing on the reset button
 
 #### Flow
 
+* **Visualise sockets**: Press `shift` key to temporarily visualize sockets
 * **New Node**: Create a node by dragging a fn from left panel into area
 * **Remove Node**: Middle click in a node to remove a node
 * **Inspect node**: Double click on a node to get detailed information
 * **Move Node**: Mouse click and drag
-* **Links**: Press [shift] and Drag from a node/socket to a socket highlighted in green
+* **Links**: Press `shift` and Drag from a node/socket to a socket highlighted in green
 * **Links(alternative)**: Toggle socket visualisation in the panel and Drag from a socket to a socket highlighted in green
 * **Remove Link**: Simple click on the link when it turns red
+
+<small>&copy; Luis Figueiredo (luisf@hexasoftware.com)</small>

+ 0 - 37
browser/vue-flow/src/assets/doc/help.md

@@ -1,37 +0,0 @@
-# Flow
-
-## How it works
-
-A flow works by requesting the previous nodes the results of its operation,
-so the starting node will be the node we want the result of, unattached nodes wont be executed
-
-All nodes are Go functions, sample with a node with one output
-
-```go
-package main
-
-import (
-	"flow/flowserver"
-	"flow/registry"
-	"net/http"
-)
-
-func main() {
-
-	r := registry.New()
-	r.Add("hello", func() string {
-		return "hello world"
-	})
-
-	http.ListenAndServe(":5000", flowserver.New(r, "storename"))
-
-}
-```
-
-Resulting in
-
-![fig1](img/s1.jpg)
-
----
-
-> WIP

二进制
browser/vue-flow/src/assets/doc/img/describing.jpg


二进制
browser/vue-flow/src/assets/doc/img/gofuncs.jpg


二进制
browser/vue-flow/src/assets/doc/img/runningjoin.jpg


二进制
browser/vue-flow/src/assets/doc/img/runningsplit.jpg


二进制
browser/vue-flow/src/assets/doc/img/sample.png


+ 191 - 0
browser/vue-flow/src/assets/doc/readme.md

@@ -0,0 +1,191 @@
+# Flow
+
+General purpose graph package for Go
+
+## Initial Idea
+
+`todo`
+
+> Explain the initial Idea came from machine learning Tensorflow etc...
+
+> And by watching drone CI stages, I saw I could implement timing and triggers
+> on nodes creating the possibility to add Go functions that handle
+> docker containers to execute our steps and triggers would execute nodes that
+> handle notifications (as plugin/slack, plugin/email)
+
+* Machine learning
+* Graphical CI pipeline
+
+## flow package
+
+Using flow package without UI
+
+```go
+package main
+
+import (
+	"flow"
+	"flow/registry"
+	"log"
+	"strings"
+)
+
+func main() {
+
+	r := registry.New()
+	r.Add(strings.Split, strings.Join)
+
+	f := flow.New()
+	f.SetRegistry(r)
+
+	op1, _ := f.Op("Split", "hello world", " ")
+	op2, _ := f.Op("Join", op1, ",")
+
+	res, err := op2.Process()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Println("res:", res)
+
+}
+```
+
+## How it works
+
+A flow works by requesting the previous nodes the results of its operation,
+so the starting node will be the node we want the result of, unattached nodes wont be executed
+
+### Registering a function
+
+All nodes are Go functions to create a node src simply add a function like:
+
+```go
+package main
+
+import (
+	"flow/flowserver"
+	"flow/registry"
+	"net/http"
+)
+
+func main() {
+
+	r := registry.New()
+	r.Add("hello", func() string {
+		return "hello world"
+	})
+
+	http.ListenAndServe(":5000", flowserver.New(r, "storename"))
+
+}
+```
+
+Resulting in
+
+![fig1](img/s1.jpg)
+
+### Go funcs
+
+Since every node is a function we can register functions from any Go
+package
+
+```go
+// Adding functions from a go package
+r.Add(strings.Split, strings.Join)
+```
+
+![fig2](img/gofuncs.jpg)
+
+### Describing funcs
+
+We can describe the inserted functions either by using `r.Add` methods or
+grouping functions with the helper `registry.Describer`
+
+```go
+// utility to apply functions to several entries
+registry.Describer(
+	r.Add(strings.Split).Inputs("str", "sep").Output("slice"),
+	r.Add(strings.Join).Inputs("slice", "sep").Output("string"),
+).Tags("strings").Extra("style", registry.M{"color": "#a77"})
+```
+
+![fig3](img/describing.jpg)
+
+### Results
+
+![fig4](img/runningsplit.jpg)
+![fig5](img/runningjoin.jpg)
+
+## Without UI
+
+```go
+package main
+
+import (
+	"flow"
+	"flow/registry"
+	"log"
+	"strings"
+)
+
+func main() {
+
+	r := registry.New()
+	r.Add(strings.Split, strings.Join)
+
+	f := flow.New()
+	f.SetRegistry(r)
+
+	op := f.Op("Join",
+		f.Op("Split", "hello world", " "),
+		",",
+	)
+
+	res, err := op.Process()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Println("res:", res)
+
+}
+```
+
+---
+
+> WIP
+
+## TODO
+
+### UX/UI
+
+* UX/UI: Create Undo behaviour
+* UX/UI: Special visualisers/nodes to display information (images, datatables, graphs)
+* UX/UI: Ability to group nodes into a single box exposing inputs and outputs
+* UX/UI: Implement touch
+* UX/UI: Drop link in node to link to the next compatible input
+
+### Packages
+
+* FlowPkg: Create training mechanism
+* FlowPkg: Machine learning examples
+
+### Backend
+
+* Backend: Export/Deploy a model to a training cluster
+* Backend: Expose the model possibly via an Rest API for easy function access
+
+### Other
+
+* Collaboration: Better concurrent editing/message passing
+
+## Ideas
+
+### Matching types
+
+in UI we can create a state such as draggingType{in:'io.Writer'} which basically
+each node can figure a match for each socket
+
+in Server side we can do an analysis on each type to see if it can be
+implemented by another type and send this as part of the registry with all
+descriptions, this way we can highlight sockets that can implement the dragging
+type

+ 11 - 2
browser/vue-flow/src/assets/style.css

@@ -45,6 +45,14 @@ button::-moz-focus-inner {
   border: 0;
 }
 
+button:disabled {
+  opacity: 0.2;
+  cursor: none;
+  pointer-events: none;
+  user-select: none;
+  filter: saturate(0.2);
+}
+
 button::after,
 .hover::after {
   content: " ";
@@ -70,8 +78,9 @@ ul {
 }
 
 small {
-  font-size: 8px;
-  color: #ccc;
+  opacity: 0.7;
+  font-size: 10px;
+  color: #555;
 }
 
 input {

+ 2 - 1
browser/vue-flow/src/components/app-flow.vue

@@ -61,7 +61,7 @@
       <h4 slot="header">INFO</h4>
       <app-info slot="body"/>
       <template slot="footer">
-        <a href="help" target="_blank">More information</a>
+        <a href="readme" target="_blank">More information</a>
         <button class="primary-inverse" @click="helpModal=false">OK</button>
       </template>
 
@@ -260,6 +260,7 @@ export default {
   flex-flow:column;
   flex:1;
   overflow:hidden;
+  transition: all var(--transition-speed);
 }
 
 .flow-panel__selector {

+ 25 - 21
browser/vue-flow/src/components/app-help.vue

@@ -1,17 +1,17 @@
 <template>
-  <div class="app-help">
-    <div class="app-help__menu">
-      <hx-tree :items="menu" container=".app-help__container"/>
+  <div class="app-readme">
+    <div class="app-readme__menu">
+      <hx-tree :items="menu" container=".app-readme__container"/>
     </div>
-    <div class="app-help__container">
+    <div class="app-readme__container">
       <div
-        class="app-help__content markdown-body"
+        class="app-readme__content markdown-body"
         v-html="content"/>
     </div>
   </div>
 </template>
 <script>
-import helpmd from '@/assets/doc/help.md'
+import readmemd from '@/assets/doc/readme.md'
 import HxTree from '@/components/shared/hx-tree'
 import 'github-markdown-css'
 import 'highlight.js/styles/monokai.css'
@@ -21,7 +21,7 @@ export default {
   data () {
     return {
       menu: [],
-      content: helpmd
+      content: readmemd
     }
   },
   mounted () {
@@ -33,6 +33,10 @@ export default {
     Array.from(elist).forEach(e => {
       const item = {name: e.innerText, link: '#' + e.getAttribute('id'), children: []}
 
+      // Improve this as levels
+      // As now the h4 can only fit in h3, h3 in h2 etc.
+      // So we could check if the new H is higher than last item/lastParent
+      // Something like recursion perhaps
       switch (e.tagName) {
         case 'H1':
           curH1 = item
@@ -54,54 +58,54 @@ export default {
 }
 </script>
 <style>
-.app-help {
+.app-readme {
   display:flex;
   flex-flow:row;
   align-items: stretch;
   width:100%;
 }
 
-.app-help__menu {
+.app-readme__menu {
   flex-grow:0;
-  min-width:170px;
+  min-width:200px;
   height:100vh;
   padding-top:100px;
   border-right: solid 1px rgba(150,150,150,0.5);
   overflow-y:auto;
 }
 
-.app-help__menu ul{
+.app-readme__menu ul{
   list-style: none;
   padding:0;
   margin:0;
   font-weight:normal;
-  padding-left:15px;
+  padding-left:12px;
   font-size:14px;
   border:none;
 }
 
-.app-help__menu > ul {
+.app-readme__menu > ul {
   font-weight:bold;
   font-size:24px;
 }
 
-.app-help__menu > ul > li > a{
+.app-readme__menu > ul > li > a{
   border-bottom: solid 1px ;
 }
 
-.app-help__menu > ul >li>ul{
+.app-readme__menu > ul >li>ul{
   padding:0;
 }
 
-.app-help__menu > ul >li>ul ul{
+.app-readme__menu > ul >li>ul ul{
   border-left: dashed 1px rgba(100,100,100,0.2);
 }
 
-.app-help__menu ul li {
+.app-readme__menu ul li {
   line-height:40px;
 }
 
-.app-help__menu ul li a {
+.app-readme__menu ul li a {
   white-space: nowrap;
   text-decoration: none;
   width:100%;
@@ -110,16 +114,16 @@ export default {
   border-bottom: solid 2px transparent;
 }
 
-.app-help__menu ul li a:hover {
+.app-readme__menu ul li a:hover {
   border-bottom: solid 2px var(--primary);
 }
 
-.app-help__container {
+.app-readme__container {
   overflow-y:auto;
 
 }
 
-.app-help__content {
+.app-readme__content {
   flex:1;
   padding:40px;
   padding-left:10%;

+ 2 - 0
browser/vue-flow/src/components/flow/editor.vue

@@ -65,6 +65,7 @@
       </flow-pan-zoom>
     </svg>
     <div class="flow-container__control">
+      <button disabled>Deploy</button>
       <button @click="stickySockets=!stickySockets"> {{ stickySockets? 'Hide':'Show' }} sockets </button>
       <button @click="stickyTriggers=!stickyTriggers"> {{ stickyTriggers? 'Hide':'Show' }} triggers </button>
       <button @click="nodeActivity=!nodeActivity"> {{ nodeActivity? 'Hide':'Show' }} activity </button>
@@ -106,6 +107,7 @@
   top: 20px;
   left: 20px;
   display:flex;
+  flex-flow:row;
   justify-content: center;
   align-items: center;
   color: #fff;

+ 0 - 1
browser/vue-flow/src/components/flow/modal-info.vue

@@ -84,7 +84,6 @@ export default {
 
 .hx-modal__container {
   width:70%;
-  height:90vh;
 }
 
 .markdown-body a {

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

@@ -1,8 +1,8 @@
 <template>
   <transition name="hx-modal">
-    <div class="hx-modal__mask" @click="$emit('close')" tabindex="1" @keydown.esc="$emit('close')">
+    <div class="hx-modal__mask" @mousedown="$emit('close')" tabindex="1" @keydown.esc="$emit('close')">
       <div class="hx-modal__wrapper" >
-        <div class="hx-modal__container" @click.stop>
+        <div class="hx-modal__container" @mousedown.stop>
           <div class="hx-modal__header">
             <slot name="header"/>
             <button

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

@@ -1,13 +1,20 @@
 import Vue from 'vue'
 import Router from 'vue-router'
 import AppFlow from '@/components/app-flow'
-import AppHelp from '@/components/app-help'
+import AppReadme from '@/components/app-readme'
 
 Vue.use(Router)
 
+// Dynamic base
+const parts = window.location.pathname.split('/')
+parts.pop()
+const base = parts.join('/') + '/'
+
 export default new Router({
   mode: 'history',
+  base: base,
   routes: [
+    { path: '/readme', component: AppReadme },
     { path: '/*', component: AppFlow }
     // { path: '/*/s\\::sessId', component: AppFlow },
     // { path: '/s\\::sessId', component: AppFlow }

+ 0 - 1
browser/vue-flow/src/services/flowservice.js

@@ -88,7 +88,6 @@ function FlowService () {
       // Schedule when is connected
       const lparam = JSON.parse(JSON.stringify(param))
       service.once('open', () => {
-        console.log('Sending the persistend: ', ftyp)
         service.send({op: ftyp, id: id, data: lparam})
       })
     }

+ 5 - 4
browser/vue-flow/src/store/ws.js

@@ -21,9 +21,10 @@ export default store => {
   store.subscribe(mut => {
     // console.log('I changed -- perform the connection somehow', mut)
     if (mut.type === 'route/ROUTE_CHANGED') {
-      // let route = mut.payload.to
-      const urlParts = mut.payload.to.path.split('/')
-      urlParts[0] = window.location.host // Substitute '/' with host
+      let route = window.location.pathname
+      // let route = mut.payload.to.path
+      const urlParts = route.split('/')
+      urlParts[0] = window.location.host // Substitute first '/' with host
       urlParts[urlParts.length - 1] = 'conn' // 'substitute last with 'conn'
       const urlPath = urlParts.join('/')
 
@@ -38,7 +39,6 @@ export default store => {
 
   // Connected
   flowService.connected(() => {
-    store.dispatch(flow.NOTIFICATION_ADD, 'Connected')
     // Allow any persisted thing to be send first if any
     setTimeout(() => {
       const match = /.*\/s:(.*)/.exec(store.state.route.path)
@@ -87,6 +87,7 @@ export default store => {
     store.dispatch(flow.NOTIFICATION_ADD, v.data)
   })
   flowService.on('sessionJoin', (v) => {
+    store.dispatch(flow.NOTIFICATION_ADD, 'Connected')
     const sessId = v.id
     store.dispatch(chat.CHAT_JOIN, {
       handle: store.state.chat.handle,

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

@@ -1,7 +1,7 @@
 var path = require('path')
 var webpack = require('webpack')
 var HtmlWebpackPlugin = require('html-webpack-plugin')
-// var CopyWebpackPlugin = require('copy-webpack-plugin')
+var CopyWebpackPlugin = require('copy-webpack-plugin')
 
 var outfile = 'index.js'
 
@@ -102,7 +102,7 @@ module.exports = {
       } */
     ]
   },
-  // plugins: [ new CopyWebpackPlugin(['static']) ],
+  plugins: [ new CopyWebpackPlugin(['src/static']) ],
   resolve: {
     alias: {
       '@': path.join(__dirname, 'src'),

+ 29 - 0
go/src/demos/cmd/noui/flow.go

@@ -0,0 +1,29 @@
+package main
+
+import (
+	"flow"
+	"flow/registry"
+	"log"
+	"strings"
+)
+
+func main() {
+
+	r := registry.New()
+	r.Add(strings.Split, strings.Join)
+
+	f := flow.New()
+	f.SetRegistry(r)
+
+	op := f.Op("Join",
+		f.Op("Split", "hello world", " "),
+		",",
+	)
+
+	res, err := op.Process()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Println("res:", res)
+
+}

+ 25 - 0
go/src/demos/cmd/simple/main.go

@@ -1,9 +1,12 @@
 package main
 
 import (
+	"flow"
 	"flow/flowserver"
 	"flow/registry"
+	"log"
 	"net/http"
+	"strings"
 )
 
 func main() {
@@ -13,6 +16,28 @@ func main() {
 		return "hello world"
 	})
 
+	// Describing functions:
+
+	// utility to apply functions to several entries
+	registry.Describer(
+		r.Add(strings.Split).Inputs("str", "sep").Output("slice"),
+		r.Add(strings.Join).Inputs("slice", "sep").Output("string"),
+	).Tags("strings").
+		Extra("style", registry.M{"color": "#a77"})
+
+	f := flow.New()
+	f.SetRegistry(r)
+
+	op := f.Op("Join",
+		f.Op("Split", "hello world", " "),
+		",",
+	)
+	res, err := op.Process()
+	if err != nil {
+		log.Fatal(err)
+	}
+	log.Println("res:", res)
+
 	http.ListenAndServe(":5000", flowserver.New(r, "storename"))
 
 }

+ 22 - 37
go/src/flow/flow.go

@@ -57,59 +57,47 @@ func (f *Flow) SetIDGen(idGen func() string) {
 	f.idGen = idGen
 }
 
-// Must Helper to return from operations
-func (f *Flow) Must(op Operation, err error) Operation {
-	if err != nil {
-		panic(err)
-	}
-	return op
-}
-
 // Auto ID generation
 
 // Op return an function operator
 //  name - a previous registered function
 //  params - the function inputs
-func (f *Flow) Op(name string, params ...interface{}) (Operation, error) {
+func (f *Flow) Op(name string, params ...interface{}) Operation {
 	var op Operation
 	var err error
 	allocErr := f.allocID(func(id string) error {
 		op, err = f.DefOp(id, name, params...)
+		if err != nil {
+			op = f.DefErrOp(id, err)
+		}
+		// Err op
 		return err
 	})
 	if allocErr != nil {
-		return nil, allocErr
+		return nil
 	}
-	return op, err
+	return op
 }
 
 // ErrOp error operation with generated ID
-func (f *Flow) ErrOp(operr error) (Operation, error) {
+func (f *Flow) ErrOp(operr error) Operation {
 	var op Operation
-	var err error
 
-	allocErr := f.allocID(func(id string) error {
-		op, err = f.DefErrOp(id, operr)
-		return err
+	f.allocID(func(id string) error {
+		op = f.DefErrOp(id, operr)
+		return nil
 	})
-	if allocErr != nil {
-		return nil, err
-	}
-	return op, err
+	return op
 }
 
 // Const returns a const operation with generated ID
-func (f *Flow) Const(value Data) (Operation, error) {
+func (f *Flow) Const(value Data) Operation {
 	var op Operation
-	var err error
-	allocErr := f.allocID(func(id string) error {
-		op, err = f.DefConst(id, value)
-		return err
+	f.allocID(func(id string) error {
+		op = f.DefConst(id, value)
+		return nil
 	})
-	if allocErr != nil {
-		return nil, allocErr
-	}
-	return op, err
+	return op
 }
 
 // Var operation
@@ -126,16 +114,13 @@ func (f *Flow) Var(name string, initial ...Data) Operation {
 }
 
 // In input operation
-func (f *Flow) In(paramID int) (Operation, error) {
+func (f *Flow) In(paramID int) Operation {
 	var op Operation
-	err := f.allocID(func(id string) error {
+	f.allocID(func(id string) error {
 		op = f.DefIn(id, paramID)
 		return nil
 	})
-	if err != nil {
-		return nil, err
-	}
-	return op, nil
+	return op
 
 }
 
@@ -250,8 +235,8 @@ func (f *Flow) allocID(fn func(id string) error) error {
 		// generate ID
 		for i := 0; i < 10; i++ {
 			id = f.idGen()
-			if _, ok := f.operations.Load(id); !ok {
-				f.operations.Store(id, nil) // tmp
+			// Load or store
+			if _, ok := f.operations.LoadOrStore(id, nil); !ok {
 				return id, nil
 			}
 		}

+ 46 - 41
go/src/flow/flow_test.go

@@ -23,13 +23,14 @@ func TestInput(t *testing.T) {
 	a := assert.A(t)
 	f := flow.New()
 
-	opIn, err := f.In(0)
-	a.Eq(err, nil, "input err should be nil")
+	opIn := f.In(0)
+	a.NotEq(opIn, nil, "input err should not be nil")
 
 	d, err := opIn.Process([]float32{2, 2, 2})
 	a.Eq(d, []float32{2, 2, 2}, "array should be equal")
 
-	op, err := f.Op("vecadd", []float32{1, 1, 1}, opIn)
+	op := f.Op("vecadd", []float32{1, 1, 1}, opIn)
+	_, err = op.Process([]float32{1, 2, 3})
 	a.Eq(err, nil, "result should not error")
 	a.NotEq(op, nil, "operation should not be nil")
 
@@ -50,10 +51,12 @@ func TestDefOp(t *testing.T) {
 	_, err = f.DefOp("1", "vecadd", []float32{1, 2, 3}, f.GetOp("2")) // r: 4 5 6
 	a.Eq(err, nil, "doing DefOp")
 
-	op, err := f.Op("vecmul", f.GetOp("1"), []float32{2, 2, 2}) //r:8 10 12
-	a.Eq(err, nil, "mul operation")
+	op := f.Op("vecmul", f.GetOp("1"), []float32{2, 2, 2}) //r:8 10 12
 	a.NotEq(op, nil, "operation not nil")
 
+	_, err = op.Process()
+	a.Eq(err, nil, "mul operation")
+
 	desired := []float32{8, 10, 12}
 	res, _ := op.Process()
 	a.Eq(res, desired, fmt.Sprintf("vector result should match:\n%v", f))
@@ -84,20 +87,20 @@ func TestIDGen(t *testing.T) {
 		return newID
 	})
 
-	i1, err := f.In(0)
-	a.Eq(err, nil, "should be nil")
+	i1 := f.In(0)
+	a.NotEq(i1, nil, "i1 should not be nil")
 	a.Eq(i1.ID(), "1", "id should be 1")
 
-	i2, err := f.In(1)
-	a.Eq(err, nil, "should be nil")
+	i2 := f.In(1)
+	a.NotEq(i2, nil, "i2 should not be nil")
 	a.Eq(i2.ID(), "2", "id should be 2")
 
-	o, err := f.Op("vecadd", i1, i2)
-	a.Eq(err, nil, "Should not nil")
+	o := f.Op("vecadd", i1, i2)
+	a.NotEq(o, nil, "Should not nil")
 	a.Eq(o.ID(), "0", "id should be 0")
 
-	o, err = f.Op("vecadd", i1, i2)
-	a.NotEq(err, nil, "Should not be nil, id generation exausted")
+	o = f.Op("vecadd", i1, i2)
+	a.Eq(o, nil, "Should be nil, id generation exausted")
 
 }
 
@@ -106,20 +109,20 @@ func TestSerialize(t *testing.T) {
 	f := flow.New()
 	var1 := f.Var("var1", []float32{4, 4, 4})
 
-	c1, _ := f.Const([]float32{1, 2, 3})
-	c2, _ := f.Const([]float32{2, 2, 2})
+	c1 := f.Const([]float32{1, 2, 3})
+	c2 := f.Const([]float32{2, 2, 2})
 
-	op1, _ := f.Op("vecmul", // op:0 - expected: [12,16,20,24]
+	op1 := f.Op("vecmul", // op:0 - expected: [12,16,20,24]
 		f.Var("vec1", []float32{4, 4, 4, 4}),
-		f.Must(f.Op("vecadd", // op:1 - expected: [3,4,5,6]
-			f.Must(f.Const([]float32{1, 2, 3, 4})),
-			f.Must(f.Const([]float32{2, 2, 2, 2})),
-		)),
+		f.Op("vecadd", // op:1 - expected: [3,4,5,6]
+			f.Const([]float32{1, 2, 3, 4}),
+			f.Const([]float32{2, 2, 2, 2}),
+		),
 	)
-	mul1, _ := f.Op("vecmul", c1, op1)               // op:2 - expected 12, 32, 60, 0
-	mul2, _ := f.Op("vecmul", mul1, var1)            // op:3 - expected 48, 128, 240, 0
-	mul3, _ := f.Op("vecmul", c2, mul2)              // op:4 - expected 96, 256, 480, 0
-	mul4, _ := f.Op("vecmul", mul3, f.Must(f.In(0))) // op:5 - expected 96, 512, 1440,0
+	mul1 := f.Op("vecmul", c1, op1)       // op:2 - expected 12, 32, 60, 0
+	mul2 := f.Op("vecmul", mul1, var1)    // op:3 - expected 48, 128, 240, 0
+	mul3 := f.Op("vecmul", c2, mul2)      // op:4 - expected 96, 256, 480, 0
+	mul4 := f.Op("vecmul", mul3, f.In(0)) // op:5 - expected 96, 512, 1440,0
 
 	s := bytes.NewBuffer(nil)
 	f.Analyse(s, []float32{1, 2, 3, 4})
@@ -143,7 +146,7 @@ func TestConst(t *testing.T) {
 	a := assert.A(t)
 	f := flow.New()
 
-	c, _ := f.Const(1)
+	c := f.Const(1)
 	res, err := c.Process()
 	a.Eq(res, 1, "It should be one")
 	a.Eq(err, nil, "const should not error")
@@ -152,11 +155,11 @@ func TestOp(t *testing.T) {
 	a := assert.A(t)
 	f := flow.New()
 
-	add, err := f.Op("vecadd",
-		f.Must(f.Op("vecmul",
+	add := f.Op("vecadd",
+		f.Op("vecmul",
 			[]float32{1, 2, 3},
 			[]float32{2, 2, 2},
-		)),
+		),
 		[]float32{1, 2, 3},
 	)
 	res, err := add.Process()
@@ -186,21 +189,20 @@ func TestCache(t *testing.T) {
 	a := assert.A(t)
 	f := flow.New()
 	{
-		r, err := f.Op("inc")
-		a.Eq(err, nil, "should not error giving operation")
+		r := f.Op("inc")
+		a.NotEq(r, nil, "should not error giving operation")
 
-		var res interface{}
 		for i := 1; i < 5; i++ {
-			res, err = r.Process()
+			res, err := r.Process()
 			a.Eq(err, nil)
 			a.Eq(res, i)
 		}
 	}
 	{
 		var res flow.Data
-		inc, _ := f.Op("inc")
+		inc := f.Op("inc")
 
-		add, _ := f.Op("add", inc, inc)
+		add := f.Op("add", inc, inc)
 		res, _ = add.Process() // 1+1
 		assert.Eq(t, res, 2)
 		res, _ = add.Process() // 2+2
@@ -228,10 +230,13 @@ func TestLocalRegistry(t *testing.T) {
 
 	f := flow.New()
 	f.SetRegistry(r)
-	op, _ := f.Op("test")
+	op := f.Op("test")
 	a.NotEq(op, nil, "operation should be valid")
 
-	op, err := f.Op("none")
+	op = f.Op("none")
+	a.NotEq(op, nil, "operation should not be nil")
+	_, err := op.Process()
+
 	a.NotEq(err, nil, "flow should contain an error")
 }
 
@@ -255,11 +260,11 @@ func prepareComplex() (*flow.Flow, flow.Operation) {
 	f1 := f.Var("f1", v1)
 	f2 := f.Var("f2", v2)
 
-	mul, _ := f.Op("vecmul", f1, f2)      // Doubles 2,4,6,8...
-	add, _ := f.Op("vecadd", mul, f2)     // Sum 4,8,10,12...
-	mul2, _ := f.Op("vecmul", mul, add)   // mul again
-	mul3, _ := f.Op("vecmul", mul2, f1)   // mul with f1
-	div1, _ := f.Op("vecdiv", mul3, mul2) // div
+	mul := f.Op("vecmul", f1, f2)      // Doubles 2,4,6,8...
+	add := f.Op("vecadd", mul, f2)     // Sum 4,8,10,12...
+	mul2 := f.Op("vecmul", mul, add)   // mul again
+	mul3 := f.Op("vecmul", mul2, f1)   // mul with f1
+	div1 := f.Op("vecdiv", mul3, mul2) // div
 
 	return f, div1
 }

+ 10 - 10
go/src/flow/flowserver/flowbuilder/builder.go

@@ -53,7 +53,7 @@ func (fb *FlowBuilder) Load(rawData []byte) *FlowBuilder {
 // Build a flow starting from node
 func (fb *FlowBuilder) Build(ID string) flow.Operation {
 	if fb.Err != nil {
-		op, _ := fb.flow.DefErrOp(ID, fb.Err)
+		op := fb.flow.DefErrOp(ID, fb.Err)
 		return op
 	}
 	f := fb.flow
@@ -62,7 +62,7 @@ func (fb *FlowBuilder) Build(ID string) flow.Operation {
 
 	if _, ok := fb.nodeTrack[ID]; ok {
 		fb.Err = ErrLoop //fmt.Errorf("[%v] Looping through nodes is disabled:", ID)
-		op, _ := fb.flow.DefErrOp(ID, fb.Err)
+		op := fb.flow.DefErrOp(ID, fb.Err)
 		return op
 	}
 	fb.nodeTrack[ID] = true
@@ -75,7 +75,7 @@ func (fb *FlowBuilder) Build(ID string) flow.Operation {
 
 	node := fb.doc.fetchNodeByID(ID)
 	if node == nil {
-		op, _ := fb.flow.DefErrOp(ID, fmt.Errorf("node not found [%v]", ID))
+		op := fb.flow.DefErrOp(ID, fmt.Errorf("node not found [%v]", ID))
 		return op
 	}
 
@@ -85,9 +85,9 @@ func (fb *FlowBuilder) Build(ID string) flow.Operation {
 	case "Input":
 		inputID, err := strconv.Atoi(node.Prop["input"])
 		if err != nil {
-			op, _ = f.DefErrOp(node.ID, errors.New("Invalid inputID value, must be a number"))
+			op = f.DefErrOp(node.ID, errors.New("Invalid inputID value, must be a number"))
 		} else {
-			op, _ = f.In(inputID) // By id perhaps
+			op = f.In(inputID) // By id perhaps
 		}
 	/*case "Variable":
 	// Input 1 is the var
@@ -102,15 +102,15 @@ func (fb *FlowBuilder) Build(ID string) flow.Operation {
 		raw := node.Label
 		val, err := parseValue(nil, raw)
 		if err != nil {
-			op, _ = f.DefErrOp(node.ID, err)
+			op = f.DefErrOp(node.ID, err)
 		} else {
-			op, _ = f.DefConst(node.ID, val)
+			op = f.DefConst(node.ID, val)
 		}
 	default:
 		// Load entry
 		entry, err := r.Entry(node.Src)
 		if err != nil {
-			op, _ = f.DefErrOp(node.ID, err)
+			op = f.DefErrOp(node.ID, err)
 			return nil
 		}
 		//// Process inputs ////
@@ -121,7 +121,7 @@ func (fb *FlowBuilder) Build(ID string) flow.Operation {
 				// Const value
 				v, err := parseValue(entry.Inputs[i], node.DefaultInputs[i])
 				if err != nil {
-					param[i], _ = f.ErrOp(err)
+					param[i] = f.ErrOp(err)
 					continue
 				}
 				param[i] = v
@@ -133,7 +133,7 @@ func (fb *FlowBuilder) Build(ID string) flow.Operation {
 
 		op, err = f.DefOp(node.ID, node.Src, param...)
 		if err != nil {
-			op, _ := f.DefErrOp(node.ID, err)
+			op := f.DefErrOp(node.ID, err)
 			return op
 		}
 		fb.addTriggersTo(node)

+ 1 - 20
go/src/flow/flowserver/flowserver.go

@@ -2,7 +2,6 @@ package flowserver
 
 import (
 	"flow/registry"
-	"log"
 	"net/http"
 	"net/http/httputil"
 	"net/url"
@@ -34,7 +33,7 @@ func New(r *registry.R, store string) *FlowServer {
 	sessionHandler = NewFlowSessionManager(r, store)
 
 	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")
 		if err != nil {
 			return nil
@@ -58,8 +57,6 @@ func New(r *registry.R, store string) *FlowServer {
 }
 
 func (f *FlowServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	log.Println("Requesting for:", r.URL)
-
 	// Manual routing
 	switch r.URL.Path {
 	case "/conn":
@@ -67,22 +64,6 @@ func (f *FlowServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	default:
 		f.staticHandler.ServeHTTP(w, r)
 	}
-	//handler, _ := f.mux.Handler(r)
-	//handler.ServeHTTP(w, r)
-	//f.mux.ServeHTTP(w, r)
-	// Manual routing here
-	// Grab last part of path
-	/*prefixToRemove := ""
-
-	urlParts := strings.Split(r.URL.Path, "/")
-	if len(urlParts) > 1 {
-		//prefixToRemove = strings.Join(urlParts[:len(urlParts)-1], "/")
-		prefixToRemove = strings.TrimRight("/"+urlParts[1], "/")
-		log.Println("Will remove", prefixToRemove)
-	}
-	//	}
-
-	http.StripPrefix(prefixToRemove, f.mux).ServeHTTP(w, r)*/
 }
 
 // ListenAndServe starts the httpserver

+ 5 - 8
go/src/flow/operation.go

@@ -245,10 +245,7 @@ func (f *Flow) DefOp(id string, name string, params ...interface{}) (Operation,
 		case *operation:
 			inputs[i] = v
 		default:
-			c, err := f.Const(v)
-			if err != nil {
-				return nil, err
-			}
+			c := f.Const(v)
 			inputs[i], _ = c.(*operation)
 		}
 	}
@@ -277,7 +274,7 @@ func (f *Flow) DefOp(id string, name string, params ...interface{}) (Operation,
 
 // DefErrOp define a nil operation that will return error
 // Usefull for builders
-func (f *Flow) DefErrOp(id string, err error) (Operation, error) {
+func (f *Flow) DefErrOp(id string, err error) Operation {
 	op := &operation{
 		Mutex:    sync.Mutex{},
 		id:       id,
@@ -289,11 +286,11 @@ func (f *Flow) DefErrOp(id string, err error) (Operation, error) {
 		executor: f.asTrigger(id, func(OpCtx, ...Data) (Data, error) { return nil, err }),
 	}
 	f.operations.Store(id, op)
-	return op, nil
+	return op
 }
 
 // DefConst define a const by defined ID
-func (f *Flow) DefConst(id string, value Data) (Operation, error) {
+func (f *Flow) DefConst(id string, value Data) Operation {
 	// Optimize this definition
 	f.consts[id] = value
 
@@ -309,7 +306,7 @@ func (f *Flow) DefConst(id string, value Data) (Operation, error) {
 	}
 	f.operations.Store(id, op)
 
-	return op, nil
+	return op
 }
 
 // DefIn define input operation