com

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jan 23, 2017 License: MIT Imports: 7 Imported by: 26

Documentation

Overview

Package com provides a registry for component objects, which are used with Go packages to create component packages. A component package is a Go package that registers a struct with com, typically named Component, that can implement hook interfaces of other components. This allows component packages to extend and hook into each other in a loosely-coupled way.

For example, imagine an issue tracking system like GitHub issues built as a package. It's completely separate from the source control system, but needs to hook into certain events to implement features like automatically closing issues mentioned in commit messages.

package issues

import "github.com/gliderlabs/comlab/pkg/com"

func init() {
  com.Register("issues", &Component{})
}

type Component struct {}

func (c *Component) GitPostCommit(commit git.Commit) {
  if issue, ok := MentionsIssue(commit.Message, []string{"closes", "fixes"}); ok {
    issue.Close()
  }
}

Registering a component with com also lets you define component-specific options or configuration that can easily be accessed from within that package. These options are populated by a configuration provider such as Viper that can read the environment or a single configuration file to configure all components. In other words, component packages can define and access their own configuration in a unified way.

package issues

import "github.com/gliderlabs/comlab/pkg/com"

func init() {
  com.Register("issues", &Component{},
    com.Option("keywords", []string{"closes", "fixes"}, "Keywords to identify issue mentions"}))
}

type Component struct {}

func (c *Component) GitPostCommit(commit gitscm.Commit) {
  if issue, ok := MentionsIssue(commit.Message, com.GetStrings("keywords")); ok {
    issue.Close()
  }
}

Components can be enabled or disabled, either through configuration or any system that implements a component context that says whether a component is enabled. This means when you implement a feature as a component, you have an easy way to turn it on and off at boot-time or at runtime, potentially per user. Components give you the foundation for feature flags.

The way you query the component registry is by interface. If your component package declared an interface, and other registered components implemented that interface, then you can get back all the enabled component objects implementing that interface. This is intended to be used as a way to expose hooks or extension points between components that can interact however you define in your interfaces.

package gitscm

import "github.com/gliderlabs/comlab/pkg/com"

func init() {
  com.Register("gitscm", &Component{})
}

type Component struct {}

type CommitObserver interface {
  GitPostCommit(commit Commit)
}

// ... rest of the gitscm package implemention here, then ...

func HandleCommit(commit Commit) {

  // ... code to handle the commit, then ...

  for _, observer := range com.Enabled(new(CommitObserver), nil) {
    observer.(CommitObserver).GitPostCommit(commit)
  }
}

You can also use the registry for pluggable modules like drivers or backends. A host component package can provide an interface that multiple registered driver components can implement, then you can select which component to use by name. Perhaps specified by the user via configuration.

package gitscm

import "github.com/gliderlabs/comlab/pkg/com"

func init() {
  com.Register("gitscm", &Component{},
    com.Option("storage_backend", "gitscm.filesystem", "Git storage backend"))
}

type Component struct {
  store StorageBackend
}

type StorageBackend interface {
  // ...
}

// ...

func (c *Component) AppInitialize() {
  backend := com.Select(com.GetString("storage_backend"), new(StorageBackend))
  if backend == nil {
    log.Fatal("Storage backend not registered:", com.GetString("storage_backend"))
  } else {
    c.store = backend.(StorageBackend)
  }
}

These minimal features work together to provide a sort of lightweight component framework, allowing you to build applications that are component-oriented, highly modular, and highly extensible.

Although com is a standalone package, it's maintained as part of Comlab, which expands on this package with tooling and patterns for building component-oriented applications with it.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Enabled

func Enabled(iface interface{}, ctx Context) []interface{}

Enabled returns enabled components that implement a particular interface. Specify an interface using new() on the interface type, like new(Interface).

Context objects are used to let you determine if a component is enabled with your own mechanisms (user profile, etc). Otherwise if nil, it will use the Context of the set ConfigProvider to determine if enabled.

func GetBool

func GetBool(name string) bool

GetBool returns a bool for the named option of the calling package's component.

func GetInt

func GetInt(name string) int

GetInt returns an integer for the named option of the calling package's component.

func GetString

func GetString(name string) string

GetString returns a string for the named option of the calling package's component.

func Option

func Option(name string, dfault interface{}, description string) interface{}

Option is used to create values used by Register to define configuration for a component. Component options are namespaced to the name the component was registered as, so no need to include extra namespacing in option names.

func Register

func Register(name string, com interface{}, options ...interface{})

Register adds a component object to the registry as the specified name. The name is usually the package name, but some package names assume the context of their parent package, so package "ui" under the "web" package might be better registered as "web.ui".

Options are optional and expect values that were returned by Option.

func Select

func Select(name string, iface interface{}) interface{}

Select returns a component registered with the specified name. The second argument lets you optionally ensure the component implements a particular interface. Specify an interface using new() on the interface type, like new(Interface). It can also just be nil to not ensure an interface.

If the selected component is not enabled or not registered, it will return nil.

func SetConfig

func SetConfig(c ConfigProvider)

SetConfig lets you specify the ConfigProvider to use. This should be called before any calls to the config getter functions.

Types

type ConfigProvider

type ConfigProvider interface {
	Context
	GetString(key string) (string, bool)
	GetInt(key string) (int, bool)
	GetBool(key string) (bool, bool)
}

ConfigProvider is the interface expected by SetConfig and is used to look up configuration. It includes Context, so a ConfigProvider must define how the underlying configuration specifies if components are enabled.

The getter methods return a value and a boolean of whether it was found. This informs com whether to use the value returned here or use the default value.

type Context

type Context interface {
	ComponentEnabled(name string) bool
}

Context interface for dynamically enabled components.

Implement this to create an object to pass into Enabled to filter out components that are not enabled, as determined by whatever mechanism you want.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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