zest

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2015 License: MIT Imports: 14 Imported by: 7

README

Zest Build Status Coverage Status Code Climate

Zest is a lightweight framework based on the Cli package allowing clean and easy command line interfaces, the Negroni middleware handler, and the Syringe injector.

Zest encourages the use of small, well chosen individual dependencies instead of high productivity, full-stack frameworks.

Overview

Having a good cli interface, a simple init/exit process and your app injected automatically should be the basis of your development.

Zest makes all that simple by aggregating well known and efficient packages. The Classic version also provides some default tools useful for most applications :

  • Gin inspired logging, CORS (allowing all origins by default) and Recovery middlewares
  • Pre-injected custom JSON renderer and Httptreemux router

Installation

To install Zest:

go get github.com/solher/zest

Launch/exit sequences

The launch sequence is divided into three steps:

  • The register sequence is run, allowing the user to register dependencies into the injector.
  • The dependency injection is run.
  • The init sequence is run, allowing the user to properly initialize the freshly built app.

Launch and exit sequences are run following the order of the array, at each start/stop of the app, thanks to Cli and the graceful shutdown module.

type ZestFunc func(z *Zest) error

type Zest struct {
	cli     *cli.App
	Context *cli.Context

	Server   *negroni.Negroni
	Injector *syringe.Syringe

	RegisterSequence []SeqFunc
	InitSequence     []SeqFunc
	ExitSequence     []SeqFunc
}

Each function is called with the Zest app in argument, allowing the user to interact with the cli context, the Negroni server or any dependency registered in the injector.

In the New version of Zest, the launch sequence only triggers the dependency injection of the app. The RegisterSequence and InitSequence arrays are empty.

In the Classic version, default register and init steps are provided:

  • classicRegister which registers the default dependencies (Render, Httptreemux) in the injector.
  • classicInit which initialize the Httptreemux router and the default middlewares in Negroni.

In both versions, the exit sequence is empty.

API errors

One of the few conventions established by Zest is the API error messages style. It allows a consistent format between the recovery middleware and the render methods, and a better expressiveness.

type APIError struct {
	Status      int    `json:"status"`
	Description string `json:"description"`
	Raw         string `json:"raw"`
	ErrorCode   string `json:"errorCode"`
}
  • The Status is a copy of the status returned in the HTTP headers.
  • The Description is the message describing what kind of error occured.
  • The Raw field is the raw error message which triggered the API error. Its purpose is to allow a more efficient debugging and should not be used as an "error id" by the API client.
  • The ErrorCode is the "machine oriented" description of the API error.

Render

The custom Render module is based on the Render package, made more expressive thanks to the coupling with the Zest API error format.

func (r *Render) JSONError(w http.ResponseWriter, status int, apiError *APIError, err error)

func (r *Render) JSON(w http.ResponseWriter, status int, object interface{})

When the JSONError method is called, the status and the error are automatically copied into the final APIError struct so you don't have to worry about that.

In situation, it looks like that :

var UnknownAPIError = &zest.APIError{Description: "An error occured. Please retry later.", ErrorCode: "UNKNOWN_ERROR"}

func (c *Controller) Handler(w http.ResponseWriter, r *http.Request, params map[string]string) {
	result, err := c.m.Action()
	if err != nil {
		c.r.JSONError(w, http.StatusInternalServerError, UnknownAPIError, err)
		return
	}

	c.r.JSON(w, http.StatusOK, result)
}

Example

main.go

package main

import (
	"github.com/dimfeld/httptreemux"
	"github.com/solher/zest"
)

func main() {
	app := zest.Classic()

	cli := app.Cli()
	cli.Name = "Test"
	cli.Usage = "This is a test application."
	app.SetCli(cli)

	app.InitSequence = append(app.InitSequence, SetRoutes)

	app.Run()
}

func SetRoutes(z *zest.Zest) error {
	d := &struct {
		Router *httptreemux.TreeMux
		Ctrl   *Controller
	}{}

	if err := z.Injector.Get(d); err != nil {
		return err
	}

	d.Router.GET("/", d.Ctrl.Handler)

	return nil
}

controller.go

package main

import (
	"net/http"

	"github.com/solher/zest"
)

func init() {
	zest.Injector.Register(NewController)
}

type Controller struct {
	m *Model
	r *zest.Render
}

func NewController(m *Model, r *zest.Render) *Controller {
	return &Controller{m: m, r: r}
}

func (c *Controller) Handler(w http.ResponseWriter, r *http.Request, params map[string]string) {
	result, err := c.m.Action()
	if err != nil {
		apiErr := &zest.APIError{Description: "An error occured. Please retry later.", ErrorCode: "UNKNOWN_ERROR"}
		c.r.JSONError(w, http.StatusInternalServerError, apiErr, err)
		return
	}

	c.r.JSON(w, http.StatusOK, result)
}

model.go

package main

import "github.com/solher/zest"

func init() {
	zest.Injector.Register(NewModel)
}

type Model struct {
	s *Store
}

func NewModel(s *Store) *Model {
	return &Model{s: s}
}

func (m *Model) Action() (string, error) {
	result, err := m.s.DBAction()

	return result, err
}

store.go

package main

import "github.com/solher/zest"

func init() {
	zest.Injector.Register(NewStore)
}

type Store struct{}

func NewStore() *Store {
	return &Store{}
}

func (s *Store) DBAction() (string, error) {
	return "foobar", nil
}

About

Thanks to the Code Gangsta for his amazing work on Negroni and Cli.

License

MIT

Documentation

Overview

Package zest contains a lightweight framework based on the codegangsta/cli package allowing clean and easy command line interfaces, the codegangsta/negroni middleware handler, and the solher/syringe injector.

Zest encourages the use of small, well chosen individual dependencies instead of high productivity, full-stack frameworks.

Index

Constants

This section is empty.

Variables

View Source
var Injector = syringe.New()

Injector provide a quick access to an instanciated injector.

Functions

This section is empty.

Types

type APIError added in v1.0.0

type APIError struct {
	Status      int    `json:"status"`
	Description string `json:"description"`
	Raw         string `json:"raw"`
	ErrorCode   string `json:"errorCode"`
}

APIError is the struct defining the format of Zest API errors.

func (APIError) Error added in v1.0.0

func (e APIError) Error() string

type Logger added in v1.0.0

type Logger struct {
	// Logger inherits from log.Logger used to log messages with the Logger middleware.
	*log.Logger
}

Logger is a middleware handler that logs the request as it goes in and the response as it goes out.

func NewLogger added in v1.0.0

func NewLogger() *Logger

NewLogger returns a new Logger instance

func (*Logger) ServeHTTP added in v1.0.0

func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

type Recovery added in v1.0.0

type Recovery struct {
	Logger    *log.Logger
	Render    *Render
	StackAll  bool
	StackSize int
}

Recovery is a Negroni inspired middleware handler that recovers from any panic and writes a 500 UNDEFINED_ERROR Zest API error.

func NewRecovery added in v1.0.0

func NewRecovery() *Recovery

NewRecovery returns a new instance of Recovery

func (*Recovery) ServeHTTP added in v1.0.0

func (rec *Recovery) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)

type Render added in v1.0.0

type Render struct {
	// contains filtered or unexported fields
}

Render is a unrolled/render based JSON/XML/HTML renderer, customized to increase the expressiveness of Zest API error rendering.

func NewRender added in v1.0.0

func NewRender() *Render

NewRender returns a new instance of Render.

func (*Render) JSON added in v1.0.0

func (r *Render) JSON(w http.ResponseWriter, status int, object interface{})

JSON writes the argument object into the response writer.

func (*Render) JSONError added in v1.0.0

func (r *Render) JSONError(w http.ResponseWriter, status int, apiError *APIError, err error)

JSONError forges and writes an APIError into the response writer.

type SeqFunc added in v1.0.0

type SeqFunc func(z *Zest) error

SeqFunc is the prototype of the functions present in the launch/exit sequences.

type Zest

type Zest struct {
	Context *cli.Context

	Server   *negroni.Negroni
	Injector *syringe.Syringe

	RegisterSequence []SeqFunc
	InitSequence     []SeqFunc
	ExitSequence     []SeqFunc
	// contains filtered or unexported fields
}

Zest is an aggregation of well known and efficient package, also providing a simple launch/exit process to the user.

The launch sequence is divided into three steps: - The register sequence is run, allowing the user to register dependencies into the injector. - The injection is run. - The init sequence is run, allowing the user to properly initialize the freshly built app. Launch and exit sequences are run following the order of the arrays, at each start/stop of the app, thanks to Cli and the tylerb/graceful module.

func Classic

func Classic() *Zest

Classic returns a new instance of Zest, with some default register and init steps: "classicRegister" which registers the default dependencies (Render, Httptreemux) in the injector. "classicInit" which initialize the Httptreemux router and the default middlewares in Negroni.

func New

func New() *Zest

New returns a new instance of Zest.

func (*Zest) Cli added in v1.0.0

func (z *Zest) Cli() cli.App

Cli returns a copy of the embedded Cli app.

func (*Zest) Run

func (z *Zest) Run()

Run starts the Cli app.

func (*Zest) SetCli added in v1.0.0

func (z *Zest) SetCli(cli cli.App)

SetCli sets a copy of the embedded Cli app.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL