hooks

package module
v0.0.0-...-951cb26 Latest Latest
Warning

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

Go to latest
Published: May 5, 2017 License: BSD-3-Clause Imports: 10 Imported by: 0

README

Hooks

This project is not ready yet.

Why?

  • Code is not commented at all.
  • Documentation is missing.
  • Export of functions that is not part of the public API.
  • Readme is just a blank paper of work.
  • Travis Integration is missing.
  • The Idea --features is not yet implemented.

Idea

  • The RegisterHook would be able to catch any parameters from the Notify. So inside RegisterHook will be the action.

  • The RegisterHook should be de-coupled from the action in order to be able to use that library when import cycle is not allowed and code refactor is not possible.

  • The RegisterHook should be able to be work as an "event listener" too, so it should be able to not work as an action too. That makes the Notify to be able to execute the action function and notify the listeners.

  • We can do both RegisterHook and Notify to accept an interface{} and transfer the function parameters or return values between RegisterHook and Notify with the reflect package help. But we will lose type safety (already losed, the library provides some helpers but it's not evaluate the action function and its parameters). So we need to:

  • Convert any function to a hook, the function can be unique with the help of runtime package (by getting the full name of the func in the source code). But if we do that we lose the de-coupling described on second paragraph.

    Possible solution:

    • Keep the hook state as uint8 and make a function which will convert the func full name to a unique number -- Or just set the sate or rename to id with type of string and we're done with the funcname -- so callers can still call and notifirs still notify without knowning each other, when needed and when no needed then the user can use the functionality without touching a lot of existing code.
  • At the future we should develop it in order to be able to set a hook in Async state in order to be executed with other 'async' hooks inside different goroutines, or inside a group of goroutines, we will see.

    Also: Be able to .Wait for specific callbacks or a group of them or globally.

  • The Notify should be able to be registered, as well, before RegisterHook in order to be able to be used when init functions are being used or when the order does not matters -- pending notifiers, remove them when RegisterHook registers the correct.

  • Be able to catch raw function execution without removing the dynamic behavior too.

    Explanation: In order to keep the statical typing feature of the language, I must think a way to add notifiers and hooks with a custom function form.

    The same function form which should be able to be splitted into:

    1. name and callback for the notifier
    2. name and payloads for the registrar

    For example:

    • /myhub contains the form which in the same time calls the (new) NotifyFunc which should gets the func and converts that to a name and calls the dynamic .Notify:
    • (myhub) func Add(item){ hub.NotifyFunc(Add, item) }
    • (notifier) myhub.Add(item)
    • (registrar) hub.RegisterFunc(myhub.Add, func(item){})

    We keep the de-coupling. The registrar doesn't knows the notifier and notifier doesn't knows about registrar at all. The registrar can import the notifier with empty statement (_ importPath), inside that init the notifier will use the myhub's notifier. Remember: The notifier executes first, which is anorthodox BUT at the previous commit I made it to be able to 'wait' for the correct .RegisterHook in order to notify the hooks. So we don't have any issue with that. The lib should be able to work both ways, as explained before (as an event listener and as a down-up notifier for func execution).

  • Prioritize per hook map's entry. Hooks are grouped to a Name (hook.Name and HooksMap's key, hook.Name is there to provide debug messages when needed).

    • The type should be an integer, but with some default Priority "levels".
    • Highest executes first.
    • No limits to the number that developer can use, by-default we will have 5-6 levels with iota * 100 starting from Idle (the lowest Priority).
    • Should be able to a hook to be prioritized from another hook, at runtime on build time, at any time it wants.
    • Should be able to accept negative values in order to reduce the priority when needed.
    • The hub will sort the routes per hook's changed priority .Name, so only hooks that are "linked" will be sorted.
    • The hook should call the hub's sort, so we should add an 'Owner *Hub' field.
  • Able to remove hook based on a hook name and its unique callback.

  • Able to remove all hooks based on a name.

License

Unless otherwise noted, the source files are distributed under the BSD-3 Clause License found in the LICENSE file.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Chain

func Chain(a ...interface{}) interface{}
Example
package main

import (
	"fmt"
)

var msgHub = NewHub()

type message struct {
	from string
	to   string
	body string
}

func newMessage(from, to, body string) message {
	fmt.Println("newMessage")
	return message{
		from: from,
		to:   to,
		body: body,
	}
}

// sender part

func send(from, to, body string) {
	msgHub.RunFunc(send, from, to, body)
}

// receiver part

func filterMessage(msg message) error {
	fmt.Printf("filtering message: %#v\n", msg)
	if msg.from == "from name" {
		return nil
	}
	return fmt.Errorf("bad message")
}

func printAnyErr(err error) {
	if err != nil {
		fmt.Println(err.Error())
	}
}

func sendValidMessage(msg message) {
	fmt.Println("Sending message...")
	fmt.Printf("%#v\n", msg)
}

func main() {

	// flow :
	// -> newMessage(from,to,body) -> filterMessage(message) -> receive(message)
	// |< if Any error then the printAnyErr will be executed and the chain will be stopped.

	msgHub.RegisterFunc(send, Chain(newMessage, filterMessage, sendValidMessage, printAnyErr))
	send("from name", "to name", "message contents")

}
Output:

newMessage
filtering message: hooks.message{from:"from name", to:"to name", body:"message contents"}
Sending message...
hooks.message{from:"from name", to:"to name", body:"message contents"}

func NameOfFunc

func NameOfFunc(fn interface{}) string

NameOfFunc returns the name of a function, developers can use that to get the name of a hook function, i.e: instead of RegisterHookFunc(myFunc), user can use RegisterHook(NameOfFunc(myFunc),...) and so on.

Types

type Hook

type Hook struct {
	// ID will be likely to be used at the future, internally, in order to transfer hooks from other machine to another
	// (yes I have plans to make it net compatible.)
	ID       string
	Owner    *Hub
	Name     string // Multiple hooks can have the same name, is the event.
	Source   Source
	Callback reflect.Value
	// if remains zero then order matters on execution,
	// they are defaulted to the "IDLE" which doesn't matters if you don't care,
	// it has nothing to do with performance, is a matter of order.
	// each group of hooks has its own group, so the priority is per Name in the HooksMap.
	//
	// Read-only value, use SetPriority if you want to alt it.
	Priority Priority
	// hiher number is the first.
	// optional descriptionist fields
	Description string
}

func (*Hook) PrioritizeAboveOf

func (h *Hook) PrioritizeAboveOf(otherHook *Hook)
Example
hub := NewHub()

hub.Register("myhook", func() {
	fmt.Println("last")
}).SetPriority(Idle) // defaults to Idle already.

hub.Register("myhook", func() {
	fmt.Println("third")
}).SetPriority(High)

hub.Register("myhook", func() {
	fmt.Println("forth")
}).SetPriority(Normal)

firstHook := hub.Register("myhook", func() {
	fmt.Println("first")
}).SetPriority(Realtime) // or anything

secondHook := hub.Register("myhook", func() {
	fmt.Println("second")
}).SetPriority(Realtime)

// even if Realtime, it can be priortized, remember we work with integers, Priority is just a type of int.
firstHook.PrioritizeAboveOf(secondHook)

hub.Run("myhook")
Output:

first
second
third
forth
last

func (*Hook) Run

func (h *Hook) Run(payloads ...interface{}) ([]reflect.Value, error)

func (*Hook) RunAsync

func (h *Hook) RunAsync(payloads ...interface{}) *HookAsyncRunner

func (*Hook) SetPriority

func (h *Hook) SetPriority(priority Priority) *Hook
Example
hub := NewHub()

hub.Register("myhook", func() {
	fmt.Println("last")
}).SetPriority(Idle) // defaults to Idle already.

hub.Register("myhook", func() {
	fmt.Println("second")
}).SetPriority(Normal)

hub.Register("myhook", func() {
	fmt.Println("third")
}).SetPriority(BelowNormal)

hub.Register("myhook", func() {
	fmt.Println("first")
}).SetPriority(High)

hub.Run("myhook")
Output:

first
second
third
last

func (*Hook) Use

func (h *Hook) Use(preProcessor interface{}) *Hook

type HookAsyncResultListener

type HookAsyncResultListener func([]reflect.Value, error)

type HookAsyncRunner

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

func (*HookAsyncRunner) OnComplete

func (runner *HookAsyncRunner) OnComplete(listeners ...HookAsyncResultListener)

type Hooks

type Hooks []*Hook

type HooksMap

type HooksMap map[string]Hooks

type Hub

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

func NewHub

func NewHub() *Hub

func (*Hub) AttachLogger

func (h *Hub) AttachLogger(logger Logger)

func (*Hub) GetHooks

func (h *Hub) GetHooks(name string) (Hooks, bool)

func (*Hub) GetHooksFunc

func (h *Hub) GetHooksFunc(fn interface{}) (Hooks, bool)

func (*Hub) Register

func (h *Hub) Register(name string, callback interface{}) *Hook
Example
var (
	myHub   = NewHub()
	SayHook = "SAY_HOOK"
	send    = func(message string) {
		myHub.Run(SayHook, message)
	}
)
// Register two listeners on the same hook "SAY_HOOK".
// Order of registrations order until one of the hook listener's
// Priority changed, as we see below.

myHub.Register(SayHook, func(message string) {
	fmt.Println("Processing the incoming message: " + message)
}).SetPriority(Idle) // default priority

myHub.Register(SayHook, func(message string) {
	fmt.Println("Incoming message: " + message)
}).SetPriority(Realtime) // highest priority

var messages = []string{
	"message1",
	"message2",
}

for _, msg := range messages {
	send(msg)
}
Output:

Incoming message: message1
Processing the incoming message: message1
Incoming message: message2
Processing the incoming message: message2

func (*Hub) RegisterFunc

func (h *Hub) RegisterFunc(hookFunc interface{}, callback interface{}) *Hook

func (*Hub) Remove

func (h *Hub) Remove(name string, callback interface{}) bool

Remove removes a hook based on a hook name and its callback.

Returns true if the removal succeed.

func (*Hub) RemoveFunc

func (h *Hub) RemoveFunc(fn interface{}, callback interface{}) bool

Remove removes a hook based on a function name and its callback.

Same as Remove(NameOfFunc(fn), callback).

Returns true if the removal succeed.

func (*Hub) RemoveHooks

func (h *Hub) RemoveHooks(name string) bool

RemoveHooks removes all registered hooks sharing the same name.

Returns true if the removal succeed.

func (*Hub) Run

func (h *Hub) Run(name string, payloads ...interface{})
Example
///TODO: fill this example with hook funcs.
fmt.Println("TODO")
Output:

TODO
Example (Second)
hub := NewHub()

hub.Register("hook1", func() {
	time.Sleep(1 * time.Second)
	fmt.Println("hey from hook1")
})

hub.Register("hook2", func() {
	fmt.Println("hey from hook2")
})

go hub.Run("hook1")
hub.Run("hook2")

// wait for hook1, it's too long, temporary we don't have a way to wait,but it's todo.
time.Sleep(2 * time.Second)

// hook2 should be printed first.
// because the hooks1' time.Sleep runs in goroutine.
//
// At the future we should develop it in order to be able to set
// the hook2 in Async state in order to be executed with other 'async'
// hooks inside different goroutines, or inside a group of goroutines, we will see.

// Also:
// Be able to .Wait for specific callbacks or a group of them or globally.
Output:

hey from hook2
hey from hook1

func (*Hub) RunFunc

func (h *Hub) RunFunc(hookFunc interface{}, payloads ...interface{})

type Logger

type Logger func(message string)

func DefaultLogger

func DefaultLogger() Logger

func NewLogger

func NewLogger(w io.Writer) Logger

type Notifier

type Notifier interface {
	Run(name string, payloads ...interface{})
}

type Priority

type Priority int
const (
	Idle Priority = iota * 100
	BelowNormal
	Normal
	AboveNormal
	High
	Realtime
)

type Registry

type Registry interface {
	Register(name string, callback interface{}) *Hook
}

type Source

type Source struct {
	Name string
	File string
	Line int
}

func GetCurrentHookSource

func GetCurrentHookSource() Source

GetCurrentHookSource returns the current hook's source who calls the current listener, it can be only called inside a hook's callback.

func GetCurrentRunner

func GetCurrentRunner() Source

GetCurrentRunner returns the caller who calls the current listener, it can be only called inside a hook's callback.

func ReadSource

func ReadSource(pointerfn uintptr) Source

func ReadSourceFunc

func ReadSourceFunc(fn interface{}) Source

Directories

Path Synopsis
_examples

Jump to

Keyboard shortcuts

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