happy

package module
v0.10.1 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2023 License: Apache-2.0 Imports: 21 Imported by: 5

README

Happy Logo

Happy Prototyping Framework and SDK

Package happy is a powerful tool for developers looking to bring their ideas to life through rapid prototyping. With its comprehensive set of resources and modular design, it's easy to create working prototypes or MVPs with minimal technical knowledge or infrastructure planning. Plus, its flexible design allows it to seamlessly integrate into projects with components written in different programming languages. So why wait? Let Happy help you achieve your goals and bring a smile to your face along the way.

Happy is very early in development phase and is not intended for production use.

PkgGoDev

Creating application

Happy SDK is designed to simplify your development process without introducing any new or additional developer tools. Your applications built with Happy can be used, built, and tested with the standard Go build tools, such as 'go test', 'go build', and 'go run'. With Happy, you have complete control over your development environment, as it will not add any third-party dependencies to your project.

Here's a simple example of how you can use Happy:

// main.go
package main

import (
  "errors"
  "github.com/happy-sdk/happy"
)

func main() {
  app := happy.New()
  app.Do(func(sess *happy.Session, args happy.Args) error {
    sess.Log().Println("Hello, world!")
    return errors.New("This is just a basic example.")
  })
  app.Main()
}

For more examples, take a look at the examples section and the examples in the ./examples/ directory."

Application api

More details of api read happy Godoc

...
// All the following are optional 
app.Before(/* called always before any command is invoked*/)
app.Do(/* root command Do function */)
app.AfterSuccess(/* called when root cmd or sub command returns without errors */)
app.AfterFailure(/* called when root cmd or sub command returns with errors */)
app.AfterAlways(/* called always when root cmd or sub command returns */)
app.OnTick(/* called while root command is blocking */)
app.OnTock(/* called while root command is blocking after tick*/)
app.OnInstall(/* optional installation step to call when user first uses your app */)
app.OnMigrate(/* optional migrations step to call when user upgrades/downgrades app */)
app.Cron(/* optional cron jobs registered for application */)
app.RegisterService(/* register standalone service to your app. */)
app.AddCommand(/* add sub command to your app. */)
app.AddFlag(/* add global flag to your app. */)
app.Setting(/* add additional, custom user settings to your app */)
...
Commands

happy.Command provides a universal API for attaching sub-commands directly to the application or providing them from an Addon.

...
cmd := happy.NewCommand(
  "my-command",
  happy.Option("usage", "My sub-command"),
)

cmd.Do(/* Main function for the command */)

// Optional:
cmd.Before(/* Called after app.Before and before cmd.Do */)
cmd.AfterSuccess(/* Called when cmd.Do returns without errors */)
cmd.AfterFailure(/* Called when cmd.Do returns with errors */)
cmd.AfterAlways(/* Called always when cmd.Do returns */)
cmd.AddSubCommand(/* Add a sub-command to the command */)
cmd.AddFlag(/* Add a flag for the command */)

app.AddCommand(cmd)
...
Services

The happy.Service API provides a flexible way to add runtime-controllable background services to your application.

...
svc := happy.NewService(
  "my-service",
  happy.Option("usage", "my custom service"),
)

svc.OnInitialize(/* Called when the app starts. */)
svc.OnStart(/* Called when the service is requested to start. */)
svc.OnStop(/* Called when the service is requested to stop. */)
svc.OnEvent(/* Called when a specific event is received. */)
svc.OnAnyEvent(/* Called when any event is received. */)
svc.Cron(/* Scheduled cron jobs to run when the service is running. */)
svc.OnTick(/* Called every tick when the service is running. */)
svc.OnTock(/* Called after every tick when the service is running. */)

app.RegisterService(svc)
...

Addons

Addons provide a simple way to bundle commands and services into a single Go package, allowing for easy sharing between projects.

// main.go
package main

import (
  "github.com/happy-sdk/happy"
  "helloworld"
)

func main() {
  app := happy.New()
  app.WithAddons(helloworld.Addon())
  app.Main()
}

// helloworld/addon.go
package helloworld

import "github.com/happy-sdk/happy"

func Addon() *happy.Addon {
  addon := happy.NewAddon(
    "hello-world",
    happy.Option("description", "example addon"),
  )

  // Optional: Set a custom setting
  addon.Setting("greet.msg", "any value", "setting description", /* validation func */)

  // Optional: Register commands provided by the addon
  addon.ProvidesCommand(...)

  // Optional: Register services provided by the addon
  addon.ProvidesService(...)

  // Optional: Make a custom API accessible across the application 
  addon.API = &HelloWorldAPI{}

  // Register all events that the addon may emit
  addon.Emits("event scope", "event key" , "event description", /* example payload */)

  // Optional callback to be called when the addon is registered
  addon.OnRegister(func(sess *happy.Session, opts *happy.Options) error {
    sess.Log().Notice("hello-world addon registered")
    return nil
  })

  return addon
}

examples

hello

most minimal usage

go run ./examples/hello/
go run ./examples/hello/ nickname
# increase verbosity
go run ./examples/hello/ --debug
go run ./examples/hello/ --system-debug
# help
go run ./examples/hello/ -h

kitchensink

main application when no subcommand is provided

go run ./examples/kitchensink/
# increase verbosity
go run ./examples/kitchensink/ --verbose
go run ./examples/kitchensink/ --debug
go run ./examples/kitchensink/ --system-debug

# main application help
go run ./examples/kitchensink/ -h

hello command with flags

go run ./examples/kitchensink/ hello --name me --repeat 10 
# or shorter
go run ./examples/kitchensink/ hello -n me -r 10 

# help for hello command
go run ./examples/kitchensink/ hello -h

Credits

GitHub contributors

Happy banner design.
Happy banner was designed by Egon Elbre egonelbre.com

Documentation

Overview

Package happy provides a modular framework for rapid prototyping in Go. With this SDK, developers of all levels can easily bring their ideas to life. Whether you're a hacker or a creator, Package happy has everything you need to tackle your domain problems and create working prototypes or MVPs with minimal technical knowledge and infrastructure planning.

Its modular design enables you to package your commands and services into reusable addons, so you're not locked into any vendor tools. It also fits well into projects where different components are written in different programming languages.

Let Package happy help you bring your projects from concept to reality and make you happy along the way.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrApplication      = errors.New("application error")
	ErrCommand          = errors.New("command error")
	ErrCommandFlags     = errors.New("command flags error")
	ErrCommandAction    = errors.New("command action error")
	ErrInvalidVersion   = errors.New("invalid version")
	ErrEngine           = errors.New("engine error")
	ErrSessionDestroyed = errors.New("session destroyed")
	ErrService          = errors.New("service error")
	ErrHappy            = errors.New("not so happy")
	ErrAddon            = errors.New("addon error")
)
View Source
var (
	ErrOption           = errors.New("option error")
	ErrOptionReadOnly   = fmt.Errorf("%w: readonly option", ErrOption)
	ErrOptionValidation = fmt.Errorf("%w: validation failed", ErrOption)
)
View Source
var OptionValidatorNotEmpty = func(key string, val vars.Value) error {
	if val.Len() == 0 {
		return fmt.Errorf("%w: %s value can not be empty", ErrOption, key)
	}
	return nil
}

Functions

func GetAPI

func GetAPI[A API](sess *Session, addonName string) (api A, err error)

Types

type API

type API interface {
	Get(key string) vars.Variable
	Set(key string, val any) error
}

type Action

type Action func(sess *Session) error

type ActionMigrate

type ActionMigrate func(ver Version, sess *Session) error

type ActionTick

type ActionTick func(sess *Session, ts time.Time, delta time.Duration) error

ActionTickFunc is operation set in given minimal time frame it can be executed. You can throttle tick/tocks to cap FPS or for [C|G]PU throttling.

Tock is helper called after each tick to separate logic processed in tick and do post processing on tick. Tocks are useful mostly for GPU ops which need to do post proccessing of frames rendered in tick.

type ActionTock

type ActionTock func(sess *Session, delta time.Duration, tps int) error

type ActionWithArgs

type ActionWithArgs func(sess *Session, args Args) error

type ActionWithEvent

type ActionWithEvent func(sess *Session, ev Event) error

type ActionWithOptions

type ActionWithOptions func(sess *Session, opts *Options) error

type Addon

type Addon struct {
	API API
	// contains filtered or unexported fields
}

func NewAddon

func NewAddon(name string, opts ...OptionArg) *Addon

func (*Addon) Emits

func (addon *Addon) Emits(scope, key, description string, example *vars.Map)

func (*Addon) EmitsEvent

func (addon *Addon) EmitsEvent(event Event)

func (*Addon) OnRegister

func (addon *Addon) OnRegister(action ActionWithOptions)

func (*Addon) ProvidesCommand

func (addon *Addon) ProvidesCommand(cmd *Command)

func (*Addon) ProvidesService

func (addon *Addon) ProvidesService(svc *Service)

func (*Addon) Setting

func (addon *Addon) Setting(key string, value any, description string, validator OptionValueValidator)

type AddonInfo

type AddonInfo struct {
	Name        string
	Description string
	Version     version.Version
}

type Application

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

func New

func New(opts ...OptionArg) *Application

New returns new happy application instance. It panics if there is critical internal error or bug.

func NewWithLogger

func NewWithLogger[L Logger[LVL], LVL LogLevelIface](logger L, level LVL, opts ...OptionArg) *Application

func (*Application) AddCommand

func (a *Application) AddCommand(cmd *Command)

func (*Application) AddFlag

func (a *Application) AddFlag(f varflag.Flag)

func (*Application) AfterAlways

func (a *Application) AfterAlways(action Action)

func (*Application) AfterFailure

func (a *Application) AfterFailure(action func(s *Session, err error) error)

func (*Application) AfterSuccess

func (a *Application) AfterSuccess(action Action)

func (*Application) Before

func (a *Application) Before(action ActionWithArgs)

func (*Application) Cron

func (a *Application) Cron(setup func(schedule CronScheduler))

func (*Application) Do

func (a *Application) Do(action ActionWithArgs)

func (*Application) Help added in v0.4.0

func (a *Application) Help(msg string)

func (*Application) Main

func (a *Application) Main()

func (*Application) OnInstall

func (a *Application) OnInstall(action Action)

func (*Application) OnMigrate

func (a *Application) OnMigrate(ver string, up, down ActionMigrate)

func (*Application) OnTick

func (a *Application) OnTick(action ActionTick)

func (*Application) OnTock

func (a *Application) OnTock(action ActionTock)

func (*Application) RegisterService

func (a *Application) RegisterService(svc *Service)

func (*Application) Setting

func (a *Application) Setting(key string, value any, description string, validator OptionValueValidator)

func (*Application) WithAddons

func (a *Application) WithAddons(addon ...*Addon)

type Args

type Args interface {
	Arg(i uint) vars.Value
	ArgDefault(i uint, value any) (vars.Value, error)
	ArgVarDefault(i uint, key string, value any) (vars.Variable, error)
	Args() []vars.Value
	Flag(name string) varflag.Flag
}

type Assets

type Assets interface{}

type Command

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

func NewCommand

func NewCommand(name string, options ...OptionArg) *Command

func (*Command) AddFlag

func (c *Command) AddFlag(f varflag.Flag)

func (*Command) AddSubCommand

func (c *Command) AddSubCommand(cmd *Command)

func (*Command) AfterAlways

func (c *Command) AfterAlways(action func(s *Session) error)

func (*Command) AfterFailure

func (c *Command) AfterFailure(action func(s *Session, err error) error)

func (*Command) AfterSuccess

func (c *Command) AfterSuccess(action func(s *Session) error)

func (*Command) Before

func (c *Command) Before(action ActionWithArgs)

func (*Command) Description

func (c *Command) Description() string

func (*Command) Do

func (c *Command) Do(action ActionWithArgs)

func (*Command) Err

func (c *Command) Err() error

func (*Command) Name

func (c *Command) Name() string

func (*Command) Parents added in v0.4.0

func (c *Command) Parents() []string

func (*Command) Usage

func (c *Command) Usage() string

type Cron

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

func (*Cron) Job

func (cs *Cron) Job(expr string, cb Action)

func (*Cron) Start

func (cs *Cron) Start() error

func (*Cron) Stop

func (cs *Cron) Stop() error

type CronScheduler

type CronScheduler interface {
	Job(expr string, cb Action)
}

type Engine

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

type Event

type Event interface {
	Key() string
	Scope() string
	Payload() *vars.Map
	Time() time.Time
}

func NewEvent

func NewEvent(scope, key string, payload *vars.Map, err error) Event

func StartServicesEvent

func StartServicesEvent(svcs ...string) Event

func StopServicesEvent

func StopServicesEvent(svcs ...string) Event

type EventListener

type EventListener interface {
	OnEvent(scope, key string, cb ActionWithEvent)
	OnAnyEvent(ActionWithEvent)
}

type LogLevel

type LogLevel int32
const (
	LevelSystemDebug    LogLevel = -10 // Level(slog.LevelDebug - 1)
	LevelDebug          LogLevel = -4  // Level(slog.LevelDebug)
	LevelInfo           LogLevel = 0   // Level(slog.LevelInfo)
	LevelTask           LogLevel = 1   // Level(slog.LevelInfo + 1)
	LevelOk             LogLevel = 2   // Level(slog.LevelInfo + 2)
	LevelNotice         LogLevel = 3   // Level(slog.LevelInfo + 3)
	LevelWarn           LogLevel = 4   // Level(slog.LevelWarn)
	LevelNotImplemented LogLevel = 5   // Level(slog.LevelWarn + 1)
	LevelDeprecated     LogLevel = 6   // Level(slog.LevelWarn + 2)
	LevelIssue          LogLevel = 7   // Level(slog.LevelWarn + 3)
	LevelError          LogLevel = 8   // Level(slog.LevelError)
	LevelBUG            LogLevel = math.MaxInt32 - 2
	LevelAlways         LogLevel = math.MaxInt32 - 1
	LevelQuiet          LogLevel = math.MaxInt32
)

func (LogLevel) Int

func (l LogLevel) Int() int

func (LogLevel) String

func (l LogLevel) String() string

String returns log level label string.

type LogLevelIface

type LogLevelIface interface {
	String() string
	Int() int
}

type Logger

type Logger[LVL LogLevelIface] interface {
	Level() LVL
	SetLevel(LVL)
}

type OptionArg

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

Option is used to define option and apply given key value to options.

func Option

func Option(key string, value any) OptionArg

Opt creates option for given key value pair which can be applied to any Options set.

type OptionKind

type OptionKind uint
const (
	ReadOnlyOption OptionKind
	SettingsOption
	ConfigOption
	RuntimeOption
)

type OptionValueValidator

type OptionValueValidator func(key string, val vars.Value) error

OptionValueValidator is callback function to validate given value, it recieves copy of value for validation. It MUST return error if validation fails, returned boolean indicates shoulkd that option be marked as radonly if validation succeeds.

type Options

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

Options is general collection of settings attached to specific application component.

func NewOptions

func NewOptions(name string, defaults []OptionArg) (*Options, error)

NewOptions returns new named options with optiona validator when provided.

func (*Options) Accepts

func (opts *Options) Accepts(key string) bool

Accepts reports whether given option key is accepted by Options.

func (*Options) Get

func (opts *Options) Get(key string) vars.Variable

func (*Options) Has

func (opts *Options) Has(key string) bool

Has reports whether options has given key

func (*Options) Load

func (opts *Options) Load(key string) (vars.Variable, bool)

func (*Options) Name

func (opts *Options) Name() string

Name is name for this Option collection.

func (*Options) Set

func (opts *Options) Set(key string, value any) error

type Service

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

func NewService

func NewService(name string, opts ...OptionArg) *Service

NewService cretes new draft service which you can compose before passing it to applciation or providing it from addon.

func (*Service) Cron

func (s *Service) Cron(setupFunc func(schedule CronScheduler))

Cron scheduled cron jobs to run when the service is running.

func (*Service) OnAnyEvent

func (s *Service) OnAnyEvent(cb ActionWithEvent)

OnAnyEvent called when any event is received.

func (*Service) OnEvent

func (s *Service) OnEvent(scope, key string, cb ActionWithEvent)

OnEvent is called when a specific event is received.

func (*Service) OnInitialize

func (s *Service) OnInitialize(action Action)

OnInitialize is called when app is preparing runtime and attaching services.

func (*Service) OnStart

func (s *Service) OnStart(action Action)

OnStart is called when service is requested to be started. For instace when command is requiring this service or whenever service is required on runtime via sess.RequireService call.

Start can be called multiple times in case of service restarts. If you do not want to allow service restarts you should implement your logic in OnStop when it's called first time and check that state OnStart.

func (*Service) OnStop

func (s *Service) OnStop(action Action)

OnStop is called when runtime request to stop the service is recieved.

func (*Service) OnTick

func (s *Service) OnTick(action ActionTick)

OnTick when set will be called every application tick when service is in running state.

func (*Service) OnTock

func (s *Service) OnTock(action ActionTock)

OnTock is called after every tick.

type ServiceInfo

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

func (*ServiceInfo) Addr

func (s *ServiceInfo) Addr() *address.Address

func (*ServiceInfo) Errs

func (s *ServiceInfo) Errs() map[time.Time]error

func (*ServiceInfo) Failed

func (s *ServiceInfo) Failed() bool

func (*ServiceInfo) Name

func (s *ServiceInfo) Name() string

func (*ServiceInfo) Running

func (s *ServiceInfo) Running() bool

func (*ServiceInfo) StartedAt

func (s *ServiceInfo) StartedAt() time.Time

func (*ServiceInfo) StoppedAt

func (s *ServiceInfo) StoppedAt() time.Time

type ServiceLoader

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

func NewServiceLoader

func NewServiceLoader(sess *Session, svcs ...string) *ServiceLoader

func (*ServiceLoader) Err

func (sl *ServiceLoader) Err() error

func (*ServiceLoader) Load

func (sl *ServiceLoader) Load() <-chan struct{}

type Session

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

func (*Session) API

func (s *Session) API(addonName string) (API, error)

func (*Session) AllowUserCancel added in v0.7.0

func (s *Session) AllowUserCancel()

AllowUserCancel allows user to cancel application by pressing Ctrl+C or sending SIGINT or SIGTERM while application is running. By default this is not allowed. If you want to allow user to cancel application, you call this method any point at application runtime. Calling this method multiple times has no effect and triggers Warning log message.

func (*Session) Closed added in v0.8.0

func (s *Session) Closed() <-chan struct{}

Closed returns channel which blocks until session is closed. It is ensured that Closed closes before root or command "Do" after functions are called. This is useful for graceful shutdown actions. e.g using together with AllowUserCancel

func (*Session) Config

func (s *Session) Config() *vars.Map

Config returns a map of all config options which are defined by application

func (*Session) Deadline

func (s *Session) Deadline() (deadline time.Time, ok bool)

Deadline returns the time when work done on behalf of this context should be canceled. Deadline returns ok==false when no deadline is set. Successive calls to Deadline return the same results.

func (*Session) Destroy

func (s *Session) Destroy(err error)

func (*Session) Dispatch

func (s *Session) Dispatch(ev Event)

func (*Session) Done

func (s *Session) Done() <-chan struct{}

Done enables you to hook into chan to know when application exits however DO NOT use that for graceful shutdown actions. Use Application.AddExitFunc or Cloesed instead.

func (*Session) Err

func (s *Session) Err() error

Err returns session error if any or nil If Done is not yet closed, Err returns nil. If Done is closed, Err returns a non-nil error explaining why: Canceled if the context was canceled or DeadlineExceeded if the context's deadline passed. After Err returns a non-nil error, successive calls to Err return the same error.

func (*Session) Get

func (s *Session) Get(key string) vars.Variable

func (*Session) Has

func (s *Session) Has(key string) bool

func (*Session) Log

func (s *Session) Log() *logging.Logger

func (*Session) Opts added in v0.6.0

func (s *Session) Opts() *vars.Map

Opts returns a map of all options which are defined by application turing current session life cycle.

func (*Session) Ready

func (s *Session) Ready() <-chan struct{}

Ready returns channel which blocks until session considers application to be ready. It is ensured that Ready closes before root or command Do function is called.

func (*Session) ServiceInfo

func (s *Session) ServiceInfo(svcurl string) (*ServiceInfo, error)

func (*Session) Set

func (s *Session) Set(key string, val any) error

func (*Session) Settings

func (s *Session) Settings() *vars.Map

Settings returns a map of all settings which are defined by application and are user configurable.

func (*Session) String

func (s *Session) String() string

func (*Session) Value

func (s *Session) Value(key any) any

Value returns the value associated with this context for key, or nil

type Version

type Version string

Directories

Path Synopsis
addons
cmd
hap Module
examples
internal
cmd/hap Module
pkg
address
Package address provides functions for working with "happy" addresses, which are URL-like strings that define the location of a resource in the "happy" system.
Package address provides functions for working with "happy" addresses, which are URL-like strings that define the location of a resource in the "happy" system.
branding Module
cli/ansicolor Module
options Module
settings Module
strings/bexp Module
strings/slug Module
vars Module
sdk
cli
Package cli provides utilities for happy command line interfaces.
Package cli provides utilities for happy command line interfaces.
commands
Package commands provides commonly used commands which you can simply plug into your application.
Package commands provides commonly used commands which you can simply plug into your application.
logattr
Package logattr is providing some common slog.Attr to use in your logging.
Package logattr is providing some common slog.Attr to use in your logging.

Jump to

Keyboard shortcuts

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