-
Notifications
You must be signed in to change notification settings - Fork 475
How to write a new backend or frontend
wego supports a flexible plugin system. There are two types of plugins: Backends and Frontends. Each has to implement a specific interface to be usable by wego. This is a short tutorial how to create a new frontend which just writes JSON formated data to stdout. Writing backends is quite similar, you just have to fill the iface.Data
struct instead of rendering it.
First you have to choose two names for the frontend. One long version, which is used as the name of the frontend itself and one short name, which is used as a unique prefix among all the frontends to ensure the namespaces don't overlap.
The long name must be used as an index to the iface.AllFrontends
map and also as the filename with the usual .go
extension.
The short name must be used for any declarations (structs, variables, consts, functions) which are visible in the whole package. Object methods, local variables and struct members are exceptions, because they are only visible with a given context other than the package.
Both names must only consist of lower case letters and the long name may use dashes -
to separate words. The long name may also contain dots .
as domain separators for backends. In our case we will choose json
for the long name and jsn
for the short prefix.
Now we create a file under the frontends/
folder named after the long name: frontends/json.go
with the following basic structure:
package frontends
import (
"github.com/schachmat/wego/iface"
)
type jsnConfig struct {
}
func (c *jsnConfig) Setup() {
}
func (c *jsnConfig) Render(r iface.Data, unitSystem iface.UnitSystem) {
}
func init() {
iface.AllFrontends["json"] = &jsnConfig{}
}
In the github.com/schachmat/wego/iface
package you can find the type definitions and see that an interface has to implement the two methods Setup()
and Render()
. In init()
, the frontend has to register itself against wego to be usable.
In this function we have to setup all our flags and they must begin with the short prefix and a -
, so in our case jsn-
. For the example frontend we only need one flag to decide if we want to indent the json output or not. We add a variable to the config struct and bind the flag to it in the Setup()
function:
type jsnConfig struct {
noIndent bool
}
func (c *jsnConfig) Setup() {
flag.BoolVar(&c.noIndent, "jsn-no-indent", false, "json frontend: do not indent the output")
}
You can also add other initialization here, but you will probably only need to setup flags here.
In this function it is our job to render the data we get in r
. We ignore the unitSystem
parameter for this interface, but it should be used to convert the values to the user desired scales with functions like unitSystem.Temp(temperatureC)
defined in iface/iface.go
.
We fill the function with our json marshalling:
func (c *jsnConfig) Render(r iface.Data, unitSystem iface.UnitSystem) {
var b []byte
var err error
if c.noIndent {
b, err = json.Marshal(r)
} else {
b, err = json.MarshalIndent(r, "", "\t")
}
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(b)
}
Note that it is ok to quit the program with log.Fatal if there is an error which cannot be gracefully ignored.
Here is the complete frontends/json.go
file:
package frontends
import (
"encoding/json"
"flag"
"log"
"os"
"github.com/schachmat/wego/iface"
)
type jsnConfig struct {
noIndent bool
}
func (c *jsnConfig) Setup() {
flag.BoolVar(&c.noIndent, "jsn-no-indent", false, "json frontend: do not indent the output")
}
func (c *jsnConfig) Render(r iface.Data, unitSystem iface.UnitSystem) {
var b []byte
var err error
if c.noIndent {
b, err = json.Marshal(r)
} else {
b, err = json.MarshalIndent(r, "", "\t")
}
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(b)
}
func init() {
iface.AllFrontends["json"] = &jsnConfig{}
}