appx

package module
v0.0.0-...-012c63d Latest Latest
Warning

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

Go to latest
Published: Oct 10, 2022 License: MIT Imports: 6 Imported by: 9

README

appx

An application framework that makes it easy to build pluggable and reusable applications.

Installation

$ go get -u github.com/RussellLuo/appx

Application Lifecycle

Simple Application:

            +-------+                   +-------+
(BEGIN) --> | Init  | --> (WORKING) --> | Clean | --> (END)
            +-------+                   +-------+

Runnable Application:

            +-------+     +-------+                   +-------+     +-------+
(BEGIN) --> | Init  | --> | Start | --> (RUNNING) --> | Stop  | --> | Clean | --> (END)
            +-------+     +-------+                   +-------+     +-------+

Examples

Documentation

Checkout the Godoc.

License

MIT

Documentation

Overview

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/RussellLuo/appx"
)

// Interface guards
var (
	_ appx.Initializer  = (*A)(nil)
	_ appx.Cleaner      = (*A)(nil)
	_ appx.StartStopper = (*A)(nil)

	_ appx.Initializer  = (*B)(nil)
	_ appx.Cleaner      = (*B)(nil)
	_ appx.StartStopper = (*B)(nil)
)

type A struct {
	Name  string
	Value string
}

func (a *A) Init(ctx appx.Context) error {
	a.Name = ctx.App.Name
	a.Value = "value_a"
	return nil
}

func (a *A) Clean() error { return nil }

func (a *A) Start(ctx context.Context) error { return nil }

func (a *A) Stop(ctx context.Context) error { return nil }

type B struct {
	Name  string
	Value string
}

func (b *B) Init(ctx appx.Context) error {
	b.Name = ctx.App.Name
	b.Value = "value_b"

	a := ctx.MustLoad("a").(*A)
	fmt.Printf("Loaded required app %q (value: %q)\n", a.Name, a.Value)
	return nil
}

func (b *B) Clean() error { return nil }

func (b *B) Start(ctx context.Context) error { return nil }

func (b *B) Stop(ctx context.Context) error { return nil }

func Logger(next appx.Standard) appx.Standard {
	return &logger{Standard: next}
}

type logger struct {
	appx.Standard
	name string
}

func (l *logger) Init(ctx appx.Context) error {
	l.name = ctx.App.Name
	fmt.Printf("Initializing app %q, which requires %d app(s)\n", l.name, len(ctx.App.Requirements()))
	return l.Standard.Init(ctx)
}

func (l *logger) Clean() error {
	fmt.Printf("Cleaning up app %q\n", l.name)
	return l.Standard.Clean()
}

func (l *logger) Start(ctx context.Context) error {
	fmt.Printf("Starting app %q\n", l.name)
	return l.Standard.Start(ctx)
}

func (l *logger) Stop(ctx context.Context) error {
	fmt.Printf("Stopping app %q\n", l.name)
	return l.Standard.Stop(ctx)
}

func main() {
	r := appx.NewRegistry()

	// Typically located in `func init()` of package a.
	r.MustRegister(appx.New("a", new(A)))

	// Typically located in `func init()` of package b.
	r.MustRegister(appx.New("b", new(B)).Require("a"))

	// Typically located in `func main()` of package main.
	r.Use(Logger)

	if err := r.Install(context.Background()); err != nil {
		fmt.Printf("err: %v\n", err)
		return
	}
	defer r.Uninstall()

	// In a typical scenario, we could just use r.Run() here. Since we
	// don't want this example to run forever, we'll use the more explicit
	// Start and Stop.
	startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	if err := r.Start(startCtx); err != nil {
		fmt.Printf("err: %v\n", err)
		return
	}

	fmt.Println("Everything is running")

	stopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	r.Stop(stopCtx)

}
Output:

Initializing app "a", which requires 0 app(s)
Initializing app "b", which requires 1 app(s)
Loaded required app "a" (value: "value_a")
Starting app "a"
Starting app "b"
Everything is running
Stopping app "b"
Stopping app "a"
Cleaning up app "b"
Cleaning up app "a"

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Graph

func Graph() map[string][]string

func Install

func Install(ctx context.Context, names ...string) error

func MustRegister

func MustRegister(app *App)

func Register

func Register(app *App) error

func Run

func Run() (os.Signal, error)

func SetOptions

func SetOptions(opts *Options)

func Start

func Start(ctx context.Context) error

func Stop

func Stop(ctx context.Context)

func Uninstall

func Uninstall()

func Use

func Use(middlewares ...func(Standard) Standard)

Types

type App

type App struct {
	Name     string   // The application name.
	Instance Standard // The user-defined application instance, which has been standardized.
	// contains filtered or unexported fields
}

App is a modular application.

func New

func New(name string, instance Instance) *App

New creates an application with the given name and the user-defined instance.

func (*App) Install

func (a *App) Install(ctx context.Context, lc Lifecycle, before, after func(*App)) (err error)

Install does the initialization work for the current application.

func (*App) Require

func (a *App) Require(names ...string) *App

Require sets the names of the applications that the current application requires.

func (*App) Requirements

func (a *App) Requirements() []string

Requirements returns the names of the applications that the current application requires.

func (*App) Uninstall

func (a *App) Uninstall() (err error)

Uninstall does the cleanup work for the current application.

func (*App) Use

func (a *App) Use(middlewares ...func(Standard) Standard) *App

Use appends one or more middlewares to the App middleware stack.

The middleware stack will execute in the order they are passed, to provide additional behaviors for the next App instance.

type Cleaner

type Cleaner interface {
	// Clean does the cleanup work for an application. It will return
	// an error if it fails.
	Clean() error
}

type Context

type Context struct {
	context.Context

	App *App // The application associated with this context.
	// contains filtered or unexported fields
}

Context is a set of context parameters used to initialize the associated application.

func (Context) Config

func (ctx Context) Config() interface{}

Config returns the configuration of the application associated with this context. It will return nil if there is no configuration.

Note that in the current implementation, in order to get the configuration successfully, Context.Config() must be called after AppConfigs has already been set by calling Registry.SetOptions().

func (Context) Load

func (ctx Context) Load(name string) (Instance, error)

Load loads the application instance specified by name. It will return an error if the given name does not refer to any required application.

func (Context) MustLoad

func (ctx Context) MustLoad(name string) Instance

MustLoad is like Load but panics if there is an error.

type Hook

type Hook struct {
	OnStart func(ctx context.Context) error
	OnStop  func(ctx context.Context) error
}

A Hook is a pair of start and stop callbacks, either of which can be nil. If a Hook's OnStart callback isn't executed (because a previous OnStart failure short-circuited application startup), its OnStop callback won't be executed.

type Initializer

type Initializer interface {
	// Init initializes an application with the given context ctx.
	// It will return an error if it fails.
	Init(ctx Context) error
}

type Instance

type Instance interface{}

Instance is a user-defined application instance.

type Instancer

type Instancer interface {
	Instance() Instance
}

type Lifecycle

type Lifecycle interface {
	Append(Hook)
}

Lifecycle allows application initializers to register callbacks that are executed on application start and stop.

The concept of lifecycle is borrowed from https://github.com/uber-go/fx.

type Options

type Options struct {
	// The timeout of application startup. Defaults to 15s.
	StartTimeout time.Duration

	// The timeout of application shutdown. Defaults to 15s.
	StopTimeout time.Duration

	// The handler for errors during the Stop and Uninstall phases.
	ErrorHandler func(error)

	// The configurations for all registered applications.
	AppConfigs map[string]interface{}
}

Options is a set of optional configurations for a registry.

type Registry

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

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates a new registry.

func (*Registry) Graph

func (r *Registry) Graph() map[string][]string

Graph generates the dependency graph for all installed applications in map form.

The format of the returned map is as below:

appName -> [dependencyAppName1, dependencyAppName2, ...]

func (*Registry) Install

func (r *Registry) Install(ctx context.Context, names ...string) error

Install installs the applications specified by names, with the given ctx. If no name is specified, all registered applications will be installed.

Note that applications will be installed in dependency order.

func (*Registry) MustRegister

func (r *Registry) MustRegister(app *App)

MustRegister is like Register but panics if there is an error.

func (*Registry) Register

func (r *Registry) Register(app *App) error

Register registers the application app into the registry.

func (*Registry) Run

func (r *Registry) Run() (os.Signal, error)

Run starts all long-running applications, blocks on the signal channel, and then gracefully stops the applications. It is designed as a shortcut of calling Start and Stop for typical usage scenarios.

The default timeout for application startup and shutdown is 15s, which can be changed by using SetConfig.

func (*Registry) SetOptions

func (r *Registry) SetOptions(opts *Options)

SetOptions sets the options for the registry.

func (*Registry) Start

func (r *Registry) Start(ctx context.Context) error

Start kicks off all long-running applications, like network servers or message queue consumers. It will returns immediately if it encounters an error.

func (*Registry) Stop

func (r *Registry) Stop(ctx context.Context)

Stop gracefully stops all long-running applications. For best-effort cleanup, It will keep going after encountering errors, and all errors will be passed to the handler specified by ErrorHandler.

func (*Registry) Uninstall

func (r *Registry) Uninstall()

Uninstall uninstalls the applications that has already been installed, in the reverse order of installation.

func (*Registry) Use

func (r *Registry) Use(middlewares ...func(Standard) Standard)

Use appends one or more middlewares to the common middleware stack, which will be applied to all registered applications.

type Standard

type Standard interface {
	Initializer
	Cleaner
	StartStopper
	Validator
	Instancer
}

Standard represents a complete interface, which includes Initializer, Cleaner, StartStopper, Validator and Instancer.

func Standardize

func Standardize(instance Instance) Standard

Standardize converts instance to a standard application instance, if it is not a standard one.

type StartStopper

type StartStopper interface {
	// Start kicks off a long-running application, like network servers or
	// message queue consumers. It will return an error if it fails.
	Start(ctx context.Context) error

	// Stop gracefully stops a long-running application. It will return an
	// error if it fails.
	Stop(ctx context.Context) error
}

type Validator

type Validator interface {
	// Validate verifies that the configuration of an application is valid.
	Validate() error
}

Jump to

Keyboard shortcuts

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