Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

define tapable #4

Closed
plastikfan opened this issue Apr 30, 2024 · 2 comments · Fixed by #6
Closed

define tapable #4

plastikfan opened this issue Apr 30, 2024 · 2 comments · Fixed by #6
Assignees
Labels
feature New feature or request

Comments

@plastikfan
Copy link
Contributor

plastikfan commented Apr 30, 2024

new package that provides the ability for internal entities to expose a set of hooks that a plugin can tap into. Will be used by bootstrap, to control how internal components participate in the start up process and by the navigator controller to allow external entities to customise the navigator.

This may be of use: go patterns.

Also, the single method interface/functional interface pattern.

@plastikfan plastikfan added the feature New feature or request label Apr 30, 2024
@plastikfan plastikfan self-assigned this Apr 30, 2024
@plastikfan
Copy link
Contributor Author

plastikfan commented May 1, 2024

After consideration, tapable requires very little implementation to the point where it is not worth a package in its own right. It's merely just a few definitions.

The intention is as follows ...

Given an interface defintion:

// Activity represents an entity that performs an action that is tapable. The type
// F should be a function signature, defined by the tapable, so F is not really
// any, but it can't be enforced to be a func, so do not try to instantiate with
// a type that is not a func.
type Activity[F any] interface {
	Tap(name string, fn F)
}

a component can declare that it wants to expose a hook to enable external customisation of a behaviour. That piece of behaviour is the unit which become tabable. So we have a Component, it's action and a Client that want to tap into this behaviour, by providing a hook.

// Widget is some domain abstraction
type Widget struct {
	Name   string
	Amount int
}

// FuncSimpleWithWidgetAndError is the action func; invoke this function
// that can be hooked/tapable. In fact, this function can either enable core
// functionality to be overridden, decorated with auxiliary behaviour or
// simply as a life-cycle event; ie we have come to a significant point in
// the component's workflow and the some external entity's function needs
// to be invoked.
type FuncSimpleWithWidgetAndError func(name string, amount int) (*Widget, error)

type ActionHook struct {
	name   string
	action FuncSimpleWithWidgetAndError
}

// Tap invoked by client to enable registration of the hook
func (h *ActionHook) Tap(_ string, fn FuncSimpleWithWidgetAndError) {
	h.action = fn
}

// Component contains tapable behaviour
type Component struct {
	hook ActionHook
}

func (c *Component) DoWork() {
	if _, err := c.hook.action("work", 0); err != nil {
		panic(fmt.Errorf("work failed: '%v'", err))
	}
}

type Client struct {
}

func (c *Client) WithComponent(component *Component) {
	component.hook.Tap("client", func(name string, amount int) (*Widget, error) {
		widget := &Widget{
			Name:   name,
			Amount: amount,
		}
		return widget, nil
	})
}

@plastikfan
Copy link
Contributor Author

To complete this properly, we really should sort out the options/registry first.

plastikfan added a commit that referenced this issue May 3, 2024
feat: add prototype tapable code (#4)
@plastikfan plastikfan linked a pull request May 3, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant