Skip to content

How to write a new backend or frontend

schachmat edited this page Mar 30, 2016 · 3 revisions

Introduction

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.

Naming

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.

Basic file structure

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.

Setup()

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.

Render()

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.

The end result

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{}
}