Development Guide
Building Go nodes by using abstract class
Use cases:
- high-level routing areas
list
nodes- areas with not a lot of CRUD
- bridges to systems that are not Go structs (e.g. DB, YAML, external REST APIs, etc.)
- part of code generation
Highlevel routing
Code
package demo
type App struct {
users *UserService
fonts *FontManager
bagels *BagelMaker
}
func NewApp() *App {
return &App{
users: &UserService{},
fonts: &FontManager{},
bagels: &BagelMaker{},
}
}
type UserService struct{}
type FontManager struct{}
type BagelMaker struct{}
class App:
def __init__(self):
self.users = {}
self.fonts = {}
self.bagels = {}
YANG
module my-app {
namespace "freeconf.org";
prefix "a";
container users {
// ... more stuff here
}
container fonts {
// ... more stuff here
}
container bagels {
// ... more stuff here
}
}
…then your node code can be this.
package demo
import (
"github.com/freeconf/yang/node"
"github.com/freeconf/yang/nodeutil"
)
func manage(a *App) node.Node {
return &nodeutil.Basic{
OnChild: func(r node.ChildRequest) (node.Node, error) {
switch r.Meta.Ident() {
case "users":
return nodeutil.ReflectChild(a.users), nil
case "fonts":
return nodeutil.ReflectChild(a.fonts), nil
case "bagels":
return nodeutil.ReflectChild(a.bagels), nil
}
return nil, nil
},
}
}
You cannot use Reflect
here because fields are private.
from freeconf import nodeutil
def manage_app(app):
def child(req):
if req.meta.ident == 'users':
return nodeutil.Node(app.users)
elif req.meta.ident == 'fonts':
return nodeutil.Node(app.fonts)
elif req.meta.ident == 'bagels':
return nodeutil.Node(app.bagels)
return None
# while this could easily be nodeutil.Node, we illustrate a Basic
# node should you want essentially an abstract class that stubs all
# this calls with reasonable default handlers
return nodeutil.Basic(on_child=child)
Additional Files
file: manage_test.go
package demo
import (
"testing"
"github.com/freeconf/yang/fc"
"github.com/freeconf/yang/node"
"github.com/freeconf/yang/nodeutil"
"github.com/freeconf/yang/parser"
"github.com/freeconf/yang/source"
)
func TestManage(t *testing.T) {
a := NewApp()
ypath := source.Dir(".")
m := parser.RequireModule(ypath, "my-app")
bwsr := node.NewBrowser(m, manage(a))
root := bwsr.Root()
defer root.Release()
actual, err := nodeutil.WriteJSON(root)
fc.AssertEqual(t, nil, err)
fc.AssertEqual(t, `{"users":{},"fonts":{},"bagels":{}}`, actual)
}
file: test_manage.py
#!/usr/bin/env python3
import unittest
from freeconf import parser, nodeutil, node, source
from manage import manage_app
from app import App
class TestManage(unittest.TestCase):
def test_manage(self):
app = App()
ypath = source.path("..")
m = parser.load_module_file(ypath, 'my-app')
mgmt = manage_app(app)
bwsr = node.Browser(m, mgmt)
root = bwsr.root()
try:
actual = nodeutil.json_write_str(root)
self.assertEqual('{"users":{},"fonts":{},"bagels":{}}', actual)
finally:
root.release()
if __name__ == '__main__':
unittest.main()